Skip to content

Conditional Templates

Lit has no special 'if' template syntax — conditional rendering is just JavaScript. This lesson covers the idiomatic patterns for showing or hiding content based on state.

Conditional Rendering Is Plain JavaScript

Because template expressions accept any JavaScript value, the most common conditional patterns from everyday JavaScript work directly inside html`...` templates: the ternary operator for either/or branches, and the && operator for 'render this or nothing'.

For more complex branching, extracting a helper method (or using the when() directive, covered in a dedicated lesson) keeps templates readable as the number of conditions grows.

render() {
  return html`
    ${this.isLoggedIn
      ? html`<p>Welcome back, ${this.username}!</p>`
      : html`<a href="/login">Log in</a>`}

    ${this.errorMessage && html`<p class="error">${this.errorMessage}</p>`}
  `;
}

The ternary picks one of two templates; && renders the error template only when errorMessage is truthy, otherwise renders false (which Lit treats as nothing).

Common Conditional Patterns

${cond ? html`A` : html`B`}      // either/or
${cond && html`A`}                // show or nothing
${cond ? html`A` : nothing}       // explicit 'nothing' branch
${this.#renderBody()}              // extracted helper method
${when(cond, () => html`A`, () => html`B`)} // when() directive
  • Prefer nothing over '' for the 'render nothing' branch — it's more explicit about intent.
  • && works because Lit treats false the same as null/undefined: nothing is rendered.
  • Avoid || for this purpose — 0 or '' as a falsy first operand can produce confusing output.
  • The when() directive (from lit/directives/when.js) is a slightly more declarative alternative to a ternary for either/or branches.

Conditional Rendering Cheat Sheet

Pick the right pattern for the situation.

Situation Recommended Pattern
Either A or B Ternary: `cond ? html`A` : html`B`
A or nothing cond && html`A` or cond ? html`A` : nothing`
Many branches Extracted helper method with if/switch
Declarative either/or when(cond, trueCase, falseCase)
Toggle visibility, keep in DOM ?hidden=${!cond} boolean attribute binding

Hiding an Element vs. Removing It Entirely

There's an important distinction between conditionally *rendering* a template (adding/removing DOM nodes entirely) and conditionally *hiding* an already-rendered element with CSS. Removing and re-adding nodes is more expensive and resets any internal state (like scroll position or focus) inside that subtree; hiding with ?hidden keeps the DOM alive.

<!-- Removes/re-adds the whole panel -->
${this.open ? html`<div class="panel">...</div>` : nothing}

<!-- Keeps the panel in the DOM, toggles visibility with CSS -->
<div class="panel" ?hidden=${!this.open}>...</div>

Prefer ?hidden for frequently toggled UI (accordions, tabs) and template removal for rarely-shown, heavy content.

Extracting Multi-Branch Logic

Once you have three or more mutually exclusive states, a chain of ternaries becomes hard to read. Extract the logic into a small helper method with an early-return if/switch structure instead.

#renderContent() {
  if (this.status === 'loading') return html`<spinner-icon></spinner-icon>`;
  if (this.status === 'error') return html`<p class="error">${this.errorMessage}</p>`;
  if (this.items.length === 0) return html`<p>No results.</p>`;
  return html`<result-list .items=${this.items}></result-list>`;
}

render() {
  return html`<section>${this.#renderContent()}</section>`;
}

Common Mistakes

  • Using || instead of && for 'show or nothing' logic and getting tripped up by falsy-but-valid values like 0.
  • Nesting several ternaries directly inside a template instead of extracting a readable helper method.
  • Removing and re-adding heavy DOM subtrees on every toggle when a CSS-based ?hidden binding would be cheaper and preserve state.
  • Forgetting that nothing needs to be imported from lit — using the string 'nothing' by mistake does not have the same effect.

Key Takeaways

  • Conditional rendering in Lit is plain JavaScript — ternaries and && cover most cases.
  • Use nothing (imported from lit) for explicit 'render nothing' branches.
  • Prefer ?hidden CSS-based toggling over full template removal for frequently toggled UI.
  • Extract multi-branch conditional logic into a helper method once it grows past two options.

Pro Tip

If a piece of UI toggles on and off frequently (like a dropdown or tooltip), default to ?hidden/?disabled bindings first, and only reach for full template removal when the hidden content is expensive enough that you'd rather not keep it in memory at all.