Built-in directives for advanced template functionality including conditional rendering, loops, styling, async operations, and performance optimization.
Directives for conditionally showing/hiding content based on conditions.
/**
* Conditionally renders content based on a boolean condition
* Supports optional false case template
*/
function when<T, F>(
condition: boolean,
trueCase: () => T,
falseCase?: () => F
): T | F | typeof nothing;Usage Examples:
import { LitElement, html } from "lit";
import { when } from "lit/directives/when.js";
class ConditionalComponent extends LitElement {
@property({ type: Boolean }) isLoggedIn = false;
@property() user = { name: "", role: "" };
render() {
return html`
${when(
this.isLoggedIn,
() => html`<p>Welcome, ${this.user.name}!</p>`,
() => html`<p>Please log in</p>`
)}
${when(
this.user.role === "admin",
() => html`<button>Admin Panel</button>`
)}
`;
}
}/**
* Renders value only if it's defined (not null or undefined)
* Useful for optional attributes
*/
function ifDefined(value: unknown): unknown | typeof nothing;Usage Examples:
import { ifDefined } from "lit/directives/if-defined.js";
class OptionalAttributes extends LitElement {
@property() title?: string;
@property() href?: string;
render() {
return html`
<a
href=${ifDefined(this.href)}
title=${ifDefined(this.title)}
>
Link text
</a>
`;
}
}/**
* Renders one of multiple templates based on a key
* Efficient for switch-like conditional rendering
*/
function choose<T>(
value: T,
cases: Array<[T, () => unknown]>,
defaultCase?: () => unknown
): unknown;Directives for rendering collections of data with keys and transformations.
/**
* Efficiently renders a list of items with optional key function
* Provides optimal DOM diffing for list updates
*/
function repeat<T>(
items: Iterable<T>,
keyFn: KeyFn<T>,
template: ItemTemplate<T>
): IterableIterator<TemplateResult>;
function repeat<T>(
items: Iterable<T>,
template: ItemTemplate<T>
): IterableIterator<TemplateResult>;
/** Key extraction function type */
type KeyFn<T> = (item: T, index: number) => unknown;
/** Item template function type */
type ItemTemplate<T> = (item: T, index: number) => unknown;Usage Examples:
import { repeat } from "lit/directives/repeat.js";
class ListComponent extends LitElement {
@property() items = [
{ id: 1, name: "Apple", price: 1.50 },
{ id: 2, name: "Banana", price: 0.75 },
{ id: 3, name: "Orange", price: 2.00 }
];
render() {
return html`
<ul>
${repeat(
this.items,
(item) => item.id, // Key function
(item, index) => html`
<li data-index=${index}>
${item.name} - $${item.price}
<button @click=${() => this._removeItem(item.id)}>Remove</button>
</li>
`
)}
</ul>
`;
}
private _removeItem(id: number) {
this.items = this.items.filter(item => item.id !== id);
}
}/**
* Maps over iterable values with a template function
* Simpler alternative to repeat when keys aren't needed
*/
function map<T>(
items: Iterable<T>,
f: (value: T, index: number) => unknown
): IterableIterator<unknown>;Usage Examples:
import { map } from "lit/directives/map.js";
class SimpleList extends LitElement {
@property() colors = ["red", "green", "blue"];
render() {
return html`
<div class="color-list">
${map(this.colors, (color, index) => html`
<div
class="color-swatch"
style="background-color: ${color}"
title="Color ${index + 1}: ${color}"
></div>
`)}
</div>
`;
}
}/**
* Joins iterable values with a separator
* Multiple overloads for different use cases
*/
function join<I, S>(
items: Iterable<I>,
joiner: S
): IterableIterator<I | S>;
function join<I, S>(
items: Iterable<I>,
joiner: (index: number) => S
): IterableIterator<I | S>;Usage Examples:
import { join } from "lit/directives/join.js";
class TagList extends LitElement {
@property() tags = ["javascript", "web-components", "lit"];
render() {
return html`
<p>Tags: ${join(this.tags, ", ")}</p>
<nav>
${join(
this.tags.map(tag => html`<a href="/tags/${tag}">${tag}</a>`),
html` | `
)}
</nav>
`;
}
}Directives for dynamic CSS class and style management.
/**
* Applies CSS classes based on object properties
* True values add the class, false/falsy values remove it
*/
function classMap(classInfo: ClassInfo): DirectiveResult;
/** Class information object type */
interface ClassInfo {
[name: string]: string | boolean | number;
}Usage Examples:
import { classMap } from "lit/directives/class-map.js";
class StyledButton extends LitElement {
@property({ type: Boolean, reflect: true }) primary = false;
@property({ type: Boolean, reflect: true }) disabled = false;
@property() size = "medium";
render() {
const classes = {
"btn": true,
"btn--primary": this.primary,
"btn--disabled": this.disabled,
[`btn--${this.size}`]: true
};
return html`
<button class=${classMap(classes)} ?disabled=${this.disabled}>
<slot></slot>
</button>
`;
}
}/**
* Applies CSS styles based on object properties
* Handles vendor prefixes and CSS custom properties
*/
function styleMap(styleInfo: StyleInfo): DirectiveResult;
/** Style information object type */
interface StyleInfo {
[name: string]: string | number | undefined | null;
}Usage Examples:
import { styleMap } from "lit/directives/style-map.js";
class DynamicStyles extends LitElement {
@property() backgroundColor = "#ffffff";
@property({ type: Number }) width = 200;
@property({ type: Number }) opacity = 1;
render() {
const styles = {
backgroundColor: this.backgroundColor,
width: `${this.width}px`,
opacity: this.opacity,
"--custom-property": this.backgroundColor
};
return html`
<div style=${styleMap(styles)}>
Dynamic styled content
</div>
`;
}
}Directives for optimizing template rendering performance.
/**
* Caches template instances based on template identity
* Prevents recreation of DOM when switching between templates
*/
function cache(value: TemplateResult): DirectiveResult;Usage Examples:
import { cache } from "lit/directives/cache.js";
class ViewSwitcher extends LitElement {
@property() currentView = "list";
private _renderListView() {
return html`<div class="list-view">List content...</div>`;
}
private _renderGridView() {
return html`<div class="grid-view">Grid content...</div>`;
}
render() {
return html`
<nav>
<button @click=${() => this.currentView = "list"}>List</button>
<button @click=${() => this.currentView = "grid"}>Grid</button>
</nav>
<main>
${cache(
this.currentView === "list"
? this._renderListView()
: this._renderGridView()
)}
</main>
`;
}
}/**
* Guards against expensive template updates
* Only re-renders when dependencies change
*/
function guard(dependencies: unknown[], templateFn: () => unknown): unknown;Usage Examples:
import { guard } from "lit/directives/guard.js";
class ExpensiveComponent extends LitElement {
@property() data = [];
@property() filter = "";
private _renderExpensiveList(items: any[]) {
// Expensive rendering logic
return html`
${items.map(item => html`
<div class="complex-item">${item.name}</div>
`)}
`;
}
render() {
return html`
<input
.value=${this.filter}
@input=${(e) => this.filter = e.target.value}
/>
${guard([this.data, this.filter], () => {
const filteredData = this.data.filter(item =>
item.name.includes(this.filter)
);
return this._renderExpensiveList(filteredData);
})}
`;
}
}/**
* Associates a key with a template for efficient updates
* Forces DOM re-creation when key changes
*/
function keyed(key: unknown, template: unknown): DirectiveResult;Directives for handling asynchronous content and streaming data.
/**
* Shows placeholder content until promise resolves
* Supports multiple promises for progressive loading
*/
function until(...values: unknown[]): DirectiveResult;Usage Examples:
import { until } from "lit/directives/until.js";
class AsyncContent extends LitElement {
@property() userId?: string;
private async _fetchUser(id: string) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
render() {
return html`
${this.userId ? until(
this._fetchUser(this.userId).then(user => html`
<div class="user-profile">
<h2>${user.name}</h2>
<p>${user.email}</p>
</div>
`),
html`<div class="loading">Loading user...</div>`
) : html`<p>No user selected</p>`}
`;
}
}/**
* Appends values from async iterable as they arrive
* Useful for streaming content
*/
function asyncAppend(asyncIterable: AsyncIterable<unknown>): DirectiveResult;
/**
* Replaces content with values from async iterable
* Shows only the latest value from the stream
*/
function asyncReplace(asyncIterable: AsyncIterable<unknown>): DirectiveResult;Usage Examples:
import { asyncAppend } from "lit/directives/async-append.js";
import { asyncReplace } from "lit/directives/async-replace.js";
class StreamingContent extends LitElement {
private async* _generateMessages() {
for (let i = 0; i < 5; i++) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield html`<p>Message ${i + 1}</p>`;
}
}
private async* _generateStatus() {
const statuses = ["Connecting...", "Loading...", "Processing...", "Complete!"];
for (const status of statuses) {
await new Promise(resolve => setTimeout(resolve, 800));
yield html`<div class="status">${status}</div>`;
}
}
render() {
return html`
<div class="messages">
${asyncAppend(this._generateMessages())}
</div>
<div class="status-area">
${asyncReplace(this._generateStatus())}
</div>
`;
}
}/**
* Generates numeric ranges
* Multiple overloads for different range specifications
*/
function range(end: number): Iterable<number>;
function range(start: number, end: number): Iterable<number>;
function range(start: number, end: number, step: number): Iterable<number>;Usage Examples:
import { range } from "lit/directives/range.js";
import { map } from "lit/directives/map.js";
class NumberList extends LitElement {
render() {
return html`
<ul>
${map(range(1, 11), (n) => html`<li>Item ${n}</li>`)}
</ul>
<div class="even-numbers">
${map(range(0, 21, 2), (n) => html`<span>${n}</span>`)}
</div>
`;
}
}/**
* Sets property/attribute only when different from live DOM value
* Prevents interference with user input
*/
function live(value: unknown): DirectiveResult;Usage Examples:
import { live } from "lit/directives/live.js";
class FormInput extends LitElement {
@property() value = "";
render() {
return html`
<input
.value=${live(this.value)}
@input=${(e) => this.value = e.target.value}
/>
`;
}
}/**
* Gets a reference to the rendered element
* Supports both Ref objects and callback functions
*/
function ref(refOrCallback: Ref | ((el: Element | undefined) => void)): DirectiveResult;
/**
* Creates a Ref object for element references
*/
function createRef<T extends Element = Element>(): Ref<T>;
/** Ref object type */
interface Ref<T extends Element = Element> {
readonly value?: T;
}
/** Ref or callback union type */
type RefOrCallback = Ref | ((el: Element | undefined) => void);Usage Examples:
import { ref, createRef } from "lit/directives/ref.js";
class RefExample extends LitElement {
private _inputRef = createRef<HTMLInputElement>();
private _focusInput() {
this._inputRef.value?.focus();
}
private _handleButtonRef = (el?: Element) => {
if (el) {
console.log("Button element:", el);
}
};
render() {
return html`
<input ${ref(this._inputRef)} type="text" />
<button
${ref(this._handleButtonRef)}
@click=${this._focusInput}
>
Focus Input
</button>
`;
}
}Directives for rendering raw HTML, SVG, and MathML content. Use with caution as these bypass Lit's automatic sanitization.
/**
* Renders raw HTML content without sanitization
* WARNING: Only use with trusted content to prevent XSS attacks
*/
function unsafeHTML(value: string): DirectiveResult;Usage Examples:
import { unsafeHTML } from "lit/directives/unsafe-html.js";
class RichContent extends LitElement {
@property() htmlContent = "";
render() {
// Only use with trusted content!
const trustedHTML = "<strong>Bold</strong> and <em>italic</em> text";
return html`
<div class="rich-content">
${unsafeHTML(trustedHTML)}
</div>
<!-- WARNING: Never do this with user input -->
<!-- ${unsafeHTML(this.htmlContent)} -->
`;
}
}/**
* Renders raw SVG content without sanitization
* WARNING: Only use with trusted content to prevent XSS attacks
*/
function unsafeSVG(value: string): DirectiveResult;Usage Examples:
import { unsafeSVG } from "lit/directives/unsafe-svg.js";
class IconRenderer extends LitElement {
@property() iconData = "";
render() {
const trustedSVG = `<circle cx="50" cy="50" r="40" fill="blue" />`;
return html`
<svg viewBox="0 0 100 100">
${unsafeSVG(trustedSVG)}
</svg>
`;
}
}/**
* Renders raw MathML content without sanitization
* WARNING: Only use with trusted content to prevent XSS attacks
*/
function unsafeMathML(value: string): DirectiveResult;Usage Examples:
import { unsafeMathML } from "lit/directives/unsafe-mathml.js";
class MathExpression extends LitElement {
@property() formula = "";
render() {
const quadraticFormula = `
<mrow>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mo>-</mo>
<mi>b</mi>
<mo>±</mo>
<msqrt>
<mrow>
<msup><mi>b</mi><mn>2</mn></msup>
<mo>-</mo>
<mn>4</mn><mi>a</mi><mi>c</mi>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>2</mn><mi>a</mi>
</mrow>
</mfrac>
</mrow>
`;
return html`
<math xmlns="http://www.w3.org/1998/Math/MathML">
${unsafeMathML(quadraticFormula)}
</math>
`;
}
}/**
* Clones and renders content from a template element
* Useful for working with existing HTML templates
*/
function templateContent(templateElement: HTMLTemplateElement): DirectiveResult;Usage Examples:
import { templateContent } from "lit/directives/template-content.js";
class TemplateRenderer extends LitElement {
@query('#existing-template') templateEl!: HTMLTemplateElement;
render() {
return html`
<!-- Existing template in DOM -->
<template id="existing-template">
<div class="legacy-content">
<h3>Legacy Template</h3>
<p>Content from existing template element</p>
</div>
</template>
<!-- Render the template content -->
<div class="rendered-content">
${this.templateEl ? templateContent(this.templateEl) : ''}
</div>
`;
}
}/** Base directive result interface */
interface DirectiveResult<T extends DirectiveClass = DirectiveClass> {
_$litDirective$: T;
values: DirectiveParameters<InstanceType<T>>;
}
/** Directive class interface */
interface DirectiveClass {
new (partInfo: PartInfo): Directive;
}
/** Part information passed to directives */
interface PartInfo {
readonly type: PartType;
readonly name?: string;
readonly strings?: ReadonlyArray<string>;
}
/** Template part types */
enum PartType {
ATTRIBUTE = 1,
CHILD = 2,
PROPERTY = 3,
BOOLEAN_ATTRIBUTE = 4,
EVENT = 5,
ELEMENT = 6
}/** Key function for repeat directive */
type KeyFn<T> = (item: T, index: number) => unknown;
/** Item template function for repeat directive */
type ItemTemplate<T> = (item: T, index: number) => unknown;
/** Class information for classMap directive */
interface ClassInfo {
[name: string]: string | boolean | number;
}
/** Style information for styleMap directive */
interface StyleInfo {
[name: string]: string | number | undefined | null;
}