Skip to content

nothing and noChange

Lit exports two special sentinel values, nothing and noChange, for precisely controlling what happens (or doesn't happen) at a given binding. This lesson explains the difference and when to use each.

Two Different Kinds of 'Do Nothing'

nothing tells Lit to render *nothing* at a given position — if something was previously rendered there, it's removed. It's the correct value to return from a conditional when you want an element, attribute, or text node to disappear entirely.

noChange is different and more advanced: it tells Lit to leave whatever was previously rendered at that position completely untouched, as if this update to that specific binding never happened. It's primarily used inside custom directives, not typical application code.

import { html, nothing } from 'lit';

render() {
  return html`
    <p class=${this.isError ? 'error' : nothing}>${this.message}</p>
  `;
}
// When isError is false, the class attribute is removed entirely,
// not set to the string "nothing".

Using nothing for the class attribute removes it cleanly, rather than leaving behind an empty or literal "nothing" string.

Importing the Sentinels

import { html, nothing, noChange } from 'lit';
  • nothing works at any binding position: text content, attribute, boolean attribute, or property.
  • At an attribute-position binding, nothing removes the attribute entirely, similar to ?attr=${false}.
  • noChange is almost never used directly in component code — it exists mainly for directive authors.
  • Returning undefined from a text-position expression behaves like nothing for rendering purposes, but using nothing explicitly is clearer intent.

nothing vs noChange Cheat Sheet

Two sentinels that look similar but solve very different problems.

Sentinel Meaning Typical Use
nothing Render/set nothing here; remove any previous content Conditional rendering, removing an attribute
noChange Leave the previous render output at this binding untouched Custom directive implementations
undefined Treated like nothing in text bindings Acceptable but less explicit than nothing
'' (empty string) Renders literally as an empty string, not the same as removing an attribute Rarely what you actually want

nothing in Everyday Component Code

The most common use of nothing is as the 'else' branch of a ternary at any binding position, whether that's text content, a class attribute, or a boolean attribute — anywhere you want 'remove this' rather than 'set this to an empty value'.

html`
  <button
    class=${this.variant ? `btn-${this.variant}` : nothing}
    aria-label=${this.label || nothing}
  >
    ${this.icon ? html`<icon-el name=${this.icon}></icon-el>` : nothing}
    ${this.text}
  </button>
`;

Three separate uses of nothing: an optional class, an optional ARIA label, and an optional icon child template.

noChange for Custom Directive Authors

When writing a custom directive (covered in the directives lessons), your directive's update()/render() method can return noChange to signal 'I decided not to update the DOM this time' — for example, a directive that only updates when a value crosses some threshold, otherwise leaving the previously rendered value in place.

import { Directive, directive, noChange } from 'lit/directive.js';

class RoundedDirective extends Directive {
  #last;
  render(value) {
    const rounded = Math.round(value);
    if (rounded === this.#last) return noChange; // skip redundant DOM writes
    this.#last = rounded;
    return rounded;
  }
}

This is an advanced pattern — most application-level components never need to return noChange directly.

Common Mistakes

  • Using an empty string '' instead of nothing when the intent is to remove an attribute entirely, leaving a stray empty attribute behind.
  • Confusing nothing (removes content) with noChange (leaves existing content as-is) — they solve opposite problems.
  • Reaching for noChange in regular component render() methods instead of in a custom directive, where it rarely makes sense.
  • Forgetting to import nothing from 'lit' and instead using the bare word nothing as an undefined variable.

Key Takeaways

  • nothing removes whatever was previously rendered at a binding position — text, attribute, or property.
  • noChange preserves the previous render output untouched, and is mainly relevant to custom directive authors.
  • Prefer nothing over an empty string when the goal is to remove an attribute or child, not set it to an empty value.
  • Both sentinels must be imported explicitly from the lit package.

Pro Tip

Whenever you write ? '' : ... or ? undefined : ... in a template as a way of hiding something, replace it with nothing — it makes the intent to 'remove this entirely' explicit both to readers and to Lit's rendering logic.