Templates and Reactivity · Lesson 6 of 25
Conditional Rendering in LWC: lwc:if, lwc:elseif, lwc:else
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:elseifandlwc: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;
}
}
▌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:elseifandlwc:elsemust come directly after thelwc:if(or anotherlwc: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';
}
}
(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/else | one 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
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; } }🥈 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.