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.
You now have an overview of directives. Next, go deep on the repeat directive for efficient keyed lists.