LearnFrenzy
Sign in Take a free test

Templates and Reactivity · Lesson 6 of 25

Conditional Rendering in LWC: lwc:if, lwc:elseif, lwc:else

5 min read Updated Nov 8, 2026 Saurabh Samir

Almost every component shows different markup in different situations: a spinner while loading, an error message, an empty state, the data. LWC handles this with three template directives – lwc:if, lwc:elseif and lwc:else – placed on <template> tags. They replaced the older if:true / if:false, which still work but are deprecated.

In this lesson

  • lwc:if, lwc:elseif and lwc:else
  • Loading, error, empty and data states
  • Migrating from if:true
  • Rules and pitfalls

The directives

<!-- opportunityStage.html -->
<template>
    <lightning-card title="Deal status">
        <template lwc:if={isClosedWon}>
            <p>🎉 Closed Won – hand over to onboarding</p>
        </template>
        <template lwc:elseif={isClosedLost}>
            <p>Closed Lost – log the reason</p>
        </template>
        <template lwc:else>
            <p>Open at {stage} – {probability}% likely</p>
        </template>
        <lightning-combobox label="Stage" value={stage} options={stages} onchange={handleStage}></lightning-combobox>
    </lightning-card>
</template>
// opportunityStage.js
import { LightningElement } from 'lwc';

const PROBABILITY = { Prospecting: 10, Negotiation: 70, 'Closed Won': 100, 'Closed Lost': 0 };

export default class OpportunityStage extends LightningElement {
    stage = 'Prospecting';
    stages = Object.keys(PROBABILITY).map((s) => ({ label: s, value: s }));

    get isClosedWon() {
        return this.stage === 'Closed Won';
    }
    get isClosedLost() {
        return this.stage === 'Closed Lost';
    }
    get probability() {
        return PROBABILITY[this.stage];
    }

    handleStage(event) {
        this.stage = event.detail.value;
    }
}

Result

▌Deal status
Open at Prospecting – 10% likely
Stage [Prospecting ▾]

› choose “Negotiation” in Stage
▌Deal status
Open at Negotiation – 70% likely
Stage [Negotiation ▾]

› choose “Closed Won” in Stage
▌Deal status
🎉 Closed Won – hand over to onboarding
Stage [Closed Won ▾]

› choose “Closed Lost” in Stage
▌Deal status
Closed Lost – log the reason
Stage [Closed Lost ▾]
  • The condition is a property or getter – again, no expressions like {stage === 'Closed Won'}.
  • lwc:elseif and lwc:else must come directly after the lwc:if (or another lwc:elseif) template – no other elements in between.
  • Markup in a false branch is not rendered at all; child components inside it aren't created until the condition becomes true.

Loading, error, empty, data

The pattern you'll write in almost every data component:

<!-- caseList.html -->
<template>
    <template lwc:if={isLoading}>
        <lightning-spinner alternative-text="Loading cases"></lightning-spinner>
    </template>
    <template lwc:elseif={error}>
        <p class="slds-text-color_error">Couldn't load cases: {error}</p>
    </template>
    <template lwc:elseif={isEmpty}>
        <p>No open cases. Nice work!</p>
    </template>
    <template lwc:else>
        <p>{cases.length} open cases, oldest: {oldestSubject}</p>
    </template>
    <lightning-button label="Simulate error" onclick={fail}></lightning-button>
    <lightning-button label="Load cases" onclick={load}></lightning-button>
    <lightning-button label="Load none" onclick={loadNone}></lightning-button>
</template>
// caseList.js
import { LightningElement } from 'lwc';

export default class CaseList extends LightningElement {
    isLoading = true;
    error;
    cases = [];

    get isEmpty() {
        return this.cases.length === 0;
    }

    get oldestSubject() {
        return this.cases[0]?.subject;
    }

    load() {
        this.isLoading = false;
        this.error = undefined;
        this.cases = [{ id: 1, subject: 'Login issue' }, { id: 2, subject: 'Invoice missing' }];
    }

    loadNone() {
        this.isLoading = false;
        this.error = undefined;
        this.cases = [];
    }

    fail() {
        this.isLoading = false;
        this.error = 'Request timed out';
    }
}

Result

(loading spinner)
[Simulate error] [Load cases] [Load none]

› click “Simulate error”
Couldn't load cases: Request timed out
[Simulate error] [Load cases] [Load none]

› click “Load none”
No open cases. Nice work!
[Simulate error] [Load cases] [Load none]

› click “Load cases”
2 open cases, oldest: Login issue
[Simulate error] [Load cases] [Load none]

Templates can read nested properties with dots ({cases.length}, {account.Owner.Name}) but not array indexes like {cases.0} – that's a compile error, so use a getter such as oldestSubject. Order the branches from most specific to the normal case, so exactly one is shown.

Migrating from if:true / if:false

Old (deprecated)New
<template if:true={isOpen}><template lwc:if={isOpen}>
<template if:false={isOpen}><template lwc:else> after the lwc:if, or a getter like isClosed
two independent if:true blocks for an if/elseone chain: lwc:if → lwc:elseif → lwc:else

The new directives are more efficient (one chain is evaluated once instead of several independent conditions) and read like the if/else you write in JavaScript. Don't mix the old and new syntax on the same chain.

Hide with CSS or remove with lwc:if?

lwc:if removes markup – child components are destroyed and recreated, losing their state. For panels that toggle often and should keep their state (a half-filled form inside a tab), toggle a CSS class such as slds-hide instead.

Practice

Try it yourself

  1. Show "Gold", "Silver" or "Bronze" for a customer score (≥ 80, ≥ 50, otherwise) using one lwc:if chain.

    Show solution
    <!-- customerTier.html -->
    <template>
        <template lwc:if={isGold}><p>🥇 Gold</p></template>
        <template lwc:elseif={isSilver}><p>🥈 Silver</p></template>
        <template lwc:else><p>🥉 Bronze</p></template>
    </template>
    // customerTier.js
    import { LightningElement, api } from 'lwc';
    
    export default class CustomerTier extends LightningElement {
        @api score = 0;
    
        get isGold() {
            return this.score >= 80;
        }
        get isSilver() {
            return this.score >= 50;
        }
    }

    Result

    🥈 Silver
    
    › parent sets score = 91
    🥇 Gold
    
    › parent sets score = 12
    🥉 Bronze

Quick check

Can you write lwc:if={count > 0}?

No – use a getter such as hasItems.

Where must lwc:else be placed?

Immediately after an lwc:if or lwc:elseif template.

Are components in a false branch created?

No – they're created when the branch becomes true and destroyed when it turns false.

Summary

  • Use lwc:if / lwc:elseif / lwc:else on template tags, with getters as conditions.
  • Model loading, error, empty and data states as one chain.
  • Replace deprecated if:true / if:false; use CSS hiding when state must survive toggling.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links