Skip to content

Lit Directives

Directives are functions that customize how a specific template expression renders or updates. This lesson introduces the concept before the dedicated lessons on repeat, classMap, styleMap, and when.

What a Directive Is

A directive is a special function, created with directive(), that you call inside a template expression instead of a plain value. Lit recognizes it and hands it fine-grained control over how that specific binding updates — deciding what to render, whether to skip an update, or how to manage state across renders — beyond what a plain value would allow.

Most developers never need to write a custom directive (covered separately in an advanced context) — the built-in directives Lit ships cover the vast majority of real-world templating needs: efficient keyed lists, conditional classes, inline styles, and either/or branching.

import { html } from 'lit';
import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js';

html`
  <div class=${classMap({ active: this.isActive, disabled: this.isDisabled })}>
    ${repeat(this.items, (item) => item.id, (item) => html`<li>${item.name}</li>`)}
  </div>
`;

classMap and repeat are both directives — regular-looking function calls placed directly inside a template expression.

Importing Built-In Directives

import { repeat } from 'lit/directives/repeat.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { guard } from 'lit/directives/guard.js';
import { live } from 'lit/directives/live.js';
  • Every built-in directive is imported individually from its own module path under lit/directives/, keeping unused directives out of your final bundle.
  • Directives are called like regular functions directly inside a ${...} expression — there's no special template syntax beyond that.
  • Directives can be used at different binding positions depending on what they do — classMap/styleMap are attribute-position only, repeat/when are typically text/child-position.
  • Directives are the extension point Lit provides for templating needs the core binding syntax doesn't cover directly.

Built-In Directives at a Glance

The directives you'll reach for most often, and what each solves.

Directive Solves
repeat Efficient, keyed list rendering that survives reordering
classMap Toggling multiple CSS classes based on an object of booleans
styleMap Setting multiple inline CSS properties from an object
when Declarative either/or conditional rendering
ifDefined Omitting an attribute entirely when a value is undefined
guard Skipping expensive re-computation unless a dependency actually changes
live Keeping a binding in sync with real user-driven DOM state (e.g. <input> typing)

Directives vs. Plain Values

A plain value in a template expression (a string, number, or template result) is rendered directly. A directive, by contrast, is a stateful helper that Lit invokes on every relevant update, giving it the chance to inspect the previous render and decide how to behave this time — which is exactly how repeat can track items by key across renders, something a plain .map() call can't do on its own.

Choosing the Right Directive for a Situation

Each built-in directive solves one specific, common problem. Recognizing which situation maps to which directive is more valuable than memorizing every directive's exact internal implementation.

Situation Directive
List reorders or contains stateful items repeat
Several CSS classes toggle based on conditions classMap
Several inline styles are computed dynamically styleMap
Either/or template branching when
An attribute should disappear when a value is missing ifDefined

Common Mistakes

  • Writing custom conditional/list logic from scratch when a built-in directive already solves the exact problem cleanly.
  • Importing an entire directives barrel file instead of each directive's specific module path, bloating the final bundle unnecessarily.
  • Using classMap/styleMap on a plain attribute-position binding instead of the class=/style= attribute specifically, where they're designed to be used.
  • Assuming directives require understanding Lit's internal directive-authoring API just to use the built-in ones — using them is much simpler than writing one.

Key Takeaways

  • Directives are functions that customize how a specific template binding updates, beyond what a plain value allows.
  • Lit ships several built-in directives covering the most common templating needs: lists, classes, styles, and conditionals.
  • Each directive is imported from its own specific module path to keep unused code out of your bundle.
  • Using a built-in directive is as simple as calling a function inside a template expression — no special syntax required.

Pro Tip

Before writing manual conditional or list-management logic inside a component, skim the list of built-in directives first — a surprising number of 'clever' template workarounds turn out to already be a one-line built-in directive call.