when() is a small directive offering a slightly more declarative alternative to a ternary for either/or conditional rendering. This lesson covers its signature and when it's worth using over a plain ternary.
when() as a Named Alternative to a Ternary
when(condition, trueCase, falseCase) evaluates condition and calls either trueCase() or falseCase() (both zero-argument functions returning a template) to produce the result. Functionally, it's equivalent to condition ? trueCase() : falseCase() — the value is primarily readability: naming the two branches explicitly can make intent clearer at a glance, especially when the condition expression itself is long.
The falseCase argument is optional; omitting it causes when() to render undefined (effectively nothing) when the condition is false, similar to using && with a ternary's missing else-branch.
import { html } from 'lit';
import { when } from 'lit/directives/when.js';
render() {
return html`
${when(
this.isLoggedIn,
() => html`<user-menu .user=${this.user}></user-menu>`,
() => html`<a href="/login">Log in</a>`
)}
`;
}
Functionally identical to a ternary, but the when(...) call reads slightly more like a sentence describing the branching logic.
when() Signature
when<T, F>(
condition: boolean,
trueCase: () => T,
falseCase?: () => F
): T | F | undefined
Both branch arguments are functions, not values directly — this means the untaken branch's template is never even constructed, which can matter if building it has a cost.
falseCase is optional; omit it for a 'render this or nothing' pattern.
when() is imported from lit/directives/when.js.
A plain ternary works identically and is often just as readable — when() is a matter of style, not a functional requirement.
when() vs Ternary Cheat Sheet
Two equivalent ways to express the same either/or logic.
Approach
Example
Ternary
`cond ? html`A` : html`B`
when()
when(cond, () => html`A`, () => html`B`)
Ternary, no else
cond ? html`A` : nothing
when(), no else
when(cond, () => html`A`)
Branches Are Lazily Evaluated Functions
Because both branches of when() are passed as functions rather than already-evaluated values, only the branch actually taken is invoked. This matters if constructing one branch's template involves any real computation — with a plain ternary, both the true and false expressions are already fully evaluated JavaScript values before the ternary even runs, whereas when()'s unused branch function is simply never called.
// With a ternary, BOTH expressions are evaluated eagerly before the ternary picks one:
cond ? html`<expensive-widget></expensive-widget>` : html`<simple-fallback></simple-fallback>`;
// (Usually fine — constructing a TemplateResult itself is cheap; only its *rendering* is deferred.)
// with when(), only the chosen branch FUNCTION is ever called:
when(cond, () => html`<expensive-widget></expensive-widget>`, () => html`<simple-fallback></simple-fallback>`);
In practice this distinction rarely matters, since constructing a TemplateResult object itself is cheap — the actual rendering work happens later regardless of which syntax you use.
Should You Actually Use when()?
Many Lit codebases use plain ternaries exclusively and never reach for when() at all — it's a legitimate stylistic choice either way. Consider when() specifically when the condition expression is long enough that a ternary's ?/: punctuation gets visually lost, or when your team's style guide simply prefers named, self-documenting branches.
Common Mistakes
Assuming when() provides some functional advantage (like automatic memoization) over a ternary — it does not; the difference is purely stylistic/readability.
Passing already-evaluated values instead of functions as the branch arguments, which is a signature/type error.
Overusing when() for simple, short conditions where a ternary is already perfectly clear.
Forgetting the optional falseCase renders undefined (nothing), which is usually fine but worth being deliberate about.
Key Takeaways
when(condition, trueCase, falseCase) is a declarative, functionally-equivalent alternative to a ternary for either/or rendering.
Both branches are passed as zero-argument functions, only one of which is actually called.
It's a stylistic choice, not a functional requirement — plain ternaries work identically for template branching.
falseCase is optional, producing 'nothing' when omitted and the condition is false.
Pro Tip
Pick one style (ternaries or when()) as a team convention and apply it consistently — mixing both styles across a codebase for the exact same kind of either/or logic adds more cognitive overhead than either style costs on its own.
You've completed the directives series. Next, learn about reactive controllers for sharing logic across components.