title | eleventyNavigation | versionLinks | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Built-in directives |
|
|
Directives are functions that can extend Lit by customizing the way an expression renders. Lit includes a number of built-in directives to help with a variety of rendering needs:
Directive | Summary |
---|---|
Styling | |
Assigns a list of classes to an element based on an object. |
|
Sets a list of style properties to an element based on an object. |
|
Loops and Conditionals | |
Renders one of two templates based on a condition. | |
Renders one of many templates based on a key value. | |
Transforms an iterable with a function. | |
Renders values from an iterable into the DOM, with optional keying to enable data diffing and DOM stability. | |
Interleave values from an iterable with a joiner value. | |
Creates an iterable of numbers in a sequence, useful for iterating a specific number of times. | |
Sets an attribute if the value is defined and removes the attribute if undefined. | |
Caching and change detection | |
Caches rendered DOM when changing templates rather than discarding the DOM. | |
Associates a renderable value with a unique key, forcing the DOM to re-render if the key changes. | |
Only re-evaluates the template when one of its dependencies changes. | |
Sets an attribute or property if it differs from the live DOM value rather than the last-rendered value. | |
Referencing rendered DOM | |
Gets a reference to an element rendered in the template. | |
Rendering special values | |
Renders the content of a |
|
Renders a string as HTML rather than text. | |
Renders a string as MathML rather than text. | |
Renders a string as SVG rather than text. | |
Asynchronous rendering | |
Renders placeholder content until one or more promises resolve. | |
Appends values from an |
|
Renders the latest value from an |
Only bundle what you use. These are called "built-in" directives because they're part of the Lit package. But each directive is a separate module, so your app only bundles the directives you import.
You can also build your own directives. For more information, see Custom directives.
Sets a list of classes to an element based on an object.
Import |
import {classMap} from 'lit/directives/class-map.js'; |
Signature |
classMap(classInfo: {[name: string]: string | boolean | number}) |
Usable location |
|
The classMap
directive uses the element.classList
API to efficiently add and
remove classes to an element based on an object passed by the user. Each key in
the object is treated as a class name, and if the value associated with the key
is truthy, that class is added to the element. On subsequent renders, any
previously set classes that are falsy or no longer in the object are removed.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@property({type: Boolean})
enabled = false;
render() {
const classes = { enabled: this.enabled, hidden: false };
return html`<div class=${classMap(classes)}>Classy text</div>`;
}
}
class MyElement extends LitElement {
static properties = {
enabled: {type: Boolean},
};
constructor() {
super();
this.enabled = false;
}
render() {
const classes = { enabled: this.enabled, hidden: false };
return html`<div class=${classMap(classes)}>Classy text</div>`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
The classMap
must be the only expression in the class
attribute, but it can
be combined with static values:
html`<div class="my-widget ${classMap(dynamicClasses)}">Static and dynamic</div>`;
Explore classMap
more in the playground.
Sets a list of style properties to an element based on an object.
Import |
import {styleMap} from 'lit/directives/style-map.js'; |
Signature |
styleMap(styleInfo: {[name: string]: string | undefined | null}) |
Usable location |
|
The styleMap
directive uses the element.style
API to efficiently add and
remove inline styles to an element based on an object passed by the user. Each
key in the object is treated as a style property name, the value is treated as
the value for that property. On subsequent renders, any previously set style
properties that are undefined or null
are removed (set to null
).
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@property({type: Boolean})
enabled = false;
render() {
const styles = { backgroundColor: this.enabled ? 'blue' : 'gray', color: 'white' };
return html`<p style=${styleMap(styles)}>Hello style!</p>`;
}
}
class MyElement extends LitElement {
static properties = {
enabled: {type: Boolean},
};
constructor() {
super();
this.enabled = false;
}
render() {
const styles = { backgroundColor: this.enabled ? 'blue' : 'gray', color: 'white' };
return html`<p style=${styleMap(styles)}>Hello style!</p>`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
For CSS properties that contain dashes, you can either use the camel-case equivalent, or put the property name in quotes. For example, you can write the CSS property font-family
as either fontFamily
or 'font-family'
:
{ fontFamily: 'roboto' }
{ 'font-family': 'roboto' }
Refer to CSS custom properties such as --custom-color
, by placing the whole property name in quotes:
{ '--custom-color': 'steelblue' }
The styleMap
must be the only expression in the style
attribute, but it can
be combined with static values:
html`<p style="color: white; ${styleMap(moreStyles)}">More styles!</p>`;
Explore styleMap
more in the playground.
Renders one of two templates based on a condition.
Import |
import {when} from 'lit/directives/when.js'; |
Signature |
when<T, F>(
condition: boolean,
trueCase: () => T,
falseCase?: () => F
) |
Usable location |
Any |
When condition
is true, returns the result of calling trueCase()
, else returns the result of calling falseCase()
if falseCase
is defined.
This is a convenience wrapper around a ternary expression that makes it a little nicer to write an inline conditional without an else.
class MyElement extends LitElement {
render() {
return html`
${when(this.user, () => html`User: ${this.user.username}`, () => html`Sign In...`)}
`;
}
}
Chooses and evaluates a template function from a list of cases based on matching
the given value
to a case.
Import |
import {choose} from 'lit/directives/choose.js'; |
Signature |
choose<T, V>(
value: T,
cases: Array<[T, () => V]>,
defaultCase?: () => V
) |
Usable location |
Any |
Cases are structured as [caseValue, func]
. value
is matched to
caseValue
by strict equality. The first match is selected. Case values
can be of any type including primitives, objects, and symbols.
This is similar to a switch statement, but as an expression and without fallthrough.
class MyElement extends LitElement {
render() {
return html`
${choose(this.section, [
['home', () => html`<h1>Home</h1>`],
['about', () => html`<h1>About</h1>`]
],
() => html`<h1>Error</h1>`)}
`;
}
}
Returns an iterable containing the result of calling f(value)
on each value in items
.
Import |
import {map} from 'lit/directives/map.js'; |
Signature |
map<T>(
items: Iterable<T> | undefined,
f: (value: T, index: number) => unknown
) |
Usable location |
Any |
map()
is a simple wrapper around a for/of loop that makes working with iterables in expressions a bit easier. map()
always updates any DOM created in place - it does not do any diffing or DOM movement. If you need that see repeat. map()
is smaller and faster than repeat()
, so if you don't need diffing and DOM stability, prefer map()
.
class MyElement extends LitElement {
render() {
return html`
<ul>
${map(items, (i) => html`<li>${i}</li>`)}
</ul>
`;
}
}
Renders values from an iterable into the DOM, with optional keying to enable data diffing and DOM stability.
Import |
import {repeat} from 'lit/directives/repeat.js'; |
Signature |
repeat(items: Iterable<T>, keyfn: KeyFn<T>, template: ItemTemplate<T>)
repeat(items: Iterable<T>, template: ItemTemplate<T>)
type KeyFn<T> = (item: T, index: number) => unknown;
type ItemTemplate<T> = (item: T, index: number) => unknown; |
Usable location |
Child expression |
Repeats a series of values (usually TemplateResults
) generated from an
iterable, and updates those items efficiently when the iterable changes. When
the keyFn
is provided, key-to-DOM association is maintained between updates by
moving generated DOM when required, and is generally the most efficient way to use repeat
since it performs minimum unnecessary work for insertions and removals.
If you're not using a key function, you should consider using map()
.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@property()
items: Array<{id: number, name: string}> = [];
render() {
return html`
<ul>
${repeat(this.items, (item) => item.id, (item, index) => html`
<li>${index}: ${item.name}</li>`)}
</ul>
`;
}
}
class MyElement extends LitElement {
static properties = {
items: {},
};
constructor() {
super();
this.items = [];
}
render() {
return html`
<ul>
${repeat(this.items, (item) => item.id, (item, index) => html`
<li>${index}: ${item.name}</li>`)}
</ul>
`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
If no keyFn
is provided, repeat
will perform similar to a simple map of
items to values, and DOM will be reused against potentially different items.
See When to use map or repeat for a discussion
of when to use repeat
and when to use standard JavaScript flow control.
Explore repeat
more in the playground.
Returns an iterable containing the values in items
interleaved with the joiner
value.
Import |
import {join} from 'lit/directives/join.js'; |
Signature |
join<I, J>(
items: Iterable<I> | undefined,
joiner: J
): Iterable<I | J>;
join<I, J>(
items: Iterable<I> | undefined,
joiner: (index: number) => J
): Iterable<I | J>; |
Usable location |
Any |
class MyElement extends LitElement {
render() {
return html`
${join(
map(menuItems, (i) => html`<a href=${i.href}>${i.label}</a>`),
html`<span class="separator">|</span>`
)}
`;
}
}
Returns an iterable of integers from start
to end
(exclusive) incrementing by step
.
Import |
import {range} from 'lit/directives/range.js'; |
Signature |
range(end: number): Iterable<number>;
range(
start: number,
end: number,
step?: number
): Iterable<number>; |
Usable location |
Any |
class MyElement extends LitElement {
render() {
return html`
${map(range(8), (i) => html`${i + 1}`)}
`;
}
}
Sets an attribute if the value is defined and removes the attribute if undefined.
Import |
import {ifDefined} from 'lit/directives/if-defined.js'; |
Signature |
ifDefined(value: unknown) |
Usable location |
Attribute expression |
For AttributeParts, sets the attribute if the value is defined and removes the attribute if the value is undefined (undefined
or null
). For other part types, this directive is a no-op.
When more than one expression exists in a single attribute value, the attribute will be removed if any expression uses ifDefined
and evaluates to undefined
/null
. This is especially useful for setting URL attributes, when the attribute should not be set if required parts of the URL are not defined, to prevent 404's.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@property()
filename: string | undefined = undefined;
@property()
size: string | undefined = undefined;
render() {
// src attribute not rendered if either size or filename are undefined
return html`<img src="/images/${ifDefined(this.size)}/${ifDefined(this.filename)}">`;
}
}
class MyElement extends LitElement {
static properties = {
filename: {},
size: {},
};
constructor() {
super();
this.filename = undefined;
this.size = undefined;
}
render() {
// src attribute not rendered if either size or filename are undefined
return html`<img src="/images/${ifDefined(this.size)}/${ifDefined(this.filename)}">`;
}
}
customElements.define('my-element', MyEleent);
{% endswitchable-sample %}
Explore ifDefined
more in the playground.
Caches rendered DOM when changing templates rather than discarding the DOM. You can use this directive to optimize rendering performance when frequently switching between large templates.
Import |
import {cache} from 'lit/directives/cache.js'; |
Signature |
cache(value: TemplateResult|unknown) |
Usable location |
Child expression |
When the value passed to cache
changes between one or more TemplateResult
s,
the rendered DOM nodes for a given template are cached when they're not in use.
When the template changes, the directive caches the current DOM nodes before
switching to the new value, and restores them from the cache when switching back
to a previously-rendered value, rather than creating the DOM nodes anew.
{% switchable-sample %}
const detailView = (data) => html`<div>...</div>`;
const summaryView = (data) => html`<div>...</div>`;
@customElement('my-element')
class MyElement extends LitElement {
@property()
data = {showDetails: true, /*...*/ };
render() {
return html`${cache(this.data.showDetails
? detailView(this.data)
: summaryView(this.data)
)}`;
}
}
const detailView = (data) => html`<div>...</div>`;
const summaryView = (data) => html`<div>...</div>`;
class MyElement extends LitElement {
static properties = {
data: {},
};
constructor() {
super();
this.data = {showDetails: true, /*...*/ };
}
render() {
return html`${cache(this.data.showDetails
? detailView(this.data)
: summaryView(this.data)
)}`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
When Lit re-renders a template, it only updates the modified portions: it doesn't create or remove any more DOM than needed. But when you switch from one template to another, Lit removes the old DOM and renders a new DOM tree.
The cache
directive caches the generated DOM for a given expression and input template. In the example above, it caches the DOM for both the summaryView
and detailView
templates. When you switch from one view to another, Lit swaps in the cached version of the new view and updates it with the latest data. This can improve rendering performance when these views are frequently switched.
Explore cache
more in the playground.
Associates a renderable value with a unique key. When the key changes, the previous DOM is removed and disposed before rendering the next value, even if the value—such as a template—is the same.
Import |
import {keyed} from 'lit/directives/keyed.js'; |
Signature |
keyed(key: unknown, value: unknown) |
Usable location |
Any expression |
keyed
is useful when you're rendering stateful elements and you need to ensure that all state of the element is cleared when some critical data changes. It essentially opts-out of Lit's default DOM reuse strategy.
keyed
is also useful in some animation scenarios if you need to force a new element for "enter" or "exit" animations.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@property()
userId: string = '';
render() {
return html`
<div>
${keyed(this.userId, html`<user-card .userId=${this.userId}></user-card>`)}
</div>`;
}
}
class MyElement extends LitElement {
static properties = {
userId: {},
};
constructor() {
super();
this.userId = '';
}
render() {
return html`
<div>
${keyed(this.userId, html`<user-card .userId=${this.userId}></user-card>`)}
</div>`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Only re-evaluates the template when one of its dependencies changes, to optimize rendering performance by preventing unnecessary work.
Import |
import {guard} from 'lit/directives/guard.js'; |
Signature |
guard(dependencies: unknown[], valueFn: () => unknown) |
Usable location |
Any expression |
Renders the value returned by valueFn
, and only re-evaluates valueFn
when one of the
dependencies changes identity.
Where:
dependencies
is an array of values to monitor for changes.valueFn
is a function that returns a renderable value.
guard
is useful with immutable data patterns, by preventing expensive work
until data updates.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@property()
value: string = '';
render() {
return html`
<div>
${guard([this.value], () => calculateSHA(this.value))}
</div>`;
}
}
class MyElement extends LitElement {
static properties = {
value: {},
};
constructor() {
super();
this.value = '';
}
render() {
return html`
<div>
${guard([this.value], () => calculateSHA(this.value))}
</div>`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
In this case, the expensive calculateSHA
function is only run when the value
property changes.
Explore guard
more in the playground.
Sets an attribute or property if it differs from the live DOM value rather than the last-rendered value.
Import |
import {live} from 'lit/directives/live.js'; |
Signature |
live(value: unknown) |
Usable location |
Attribute or property expression |
When determining whether to update the value, checks the expression value against the live DOM value, instead of Lit's default behavior of checking against the last set value.
This is useful for cases where the DOM value may change from outside of Lit. For
example, when using an expression to set an <input>
element's value
property, a content editable element's text, or to a custom element that changes
its own properties or attributes.
In these cases if the DOM value changes, but the value set through Lit
expression hasn't, Lit won't know to update the DOM value and will leave it
alone. If this is not what you want—if you want to overwrite the DOM value with
the bound value no matter what—use the live()
directive.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@property()
data = {value: 'test'};
render() {
return html`<input .value=${live(this.data.value)}>`;
}
}
class MyElement extends LitElement {
static properties = {
data: {},
};
constructor() {
super();
this.data = {value: 'test'};
}
render() {
return html`<input .value=${live(this.data.value)}>`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
live()
performs a strict equality check against the live DOM value, and if
the new value is equal to the live value, does nothing. This means that
live()
should not be used when the expression will cause a type conversion. If
you use live()
with an attribute expression, make sure that only strings are
passed in, or the expression will update every render.
Explore live
more in the playground.
Renders the content of a <template>
element.
Import |
import {templateContent} from 'lit/directives/template-content.js'; |
Signature |
templateContent(templateElement: HTMLTemplateElement) |
Usable location |
Child expression |
Lit templates are encoded in Javascript, so that they can embed Javascript
expressions that make them dynamic. If you have a static HTML <template>
that
you need to include in your Lit template, you can use the templateContent
directive to clone the template content and include it in your Lit template. As
long as the template element reference does not change between renders,
subsequent renders will no-op.
Note, the template content should be developer-controlled and must not be created using an untrusted string. Examples of untrusted content include query string parameters and values from user inputs. Untrusted templates rendered with this directive could lead to cross-site scripting (XSS) vulnerabilities.
{% switchable-sample %}
const templateEl = document.querySelector('template#myContent') as HTMLTemplateElement;
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
Here's some content from a template element:
${templateContent(templateEl)}`;
}
}
const templateEl = document.querySelector('template#myContent');
class MyElement extends LitElement {
render() {
return html`
Here's some content from a template element:
${templateContent(templateEl)}`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Explore templateContent
more in the playground.
Renders a string as HTML rather than text.
Import |
import {unsafeHTML} from 'lit/directives/unsafe-html.js'; |
Signature |
unsafeHTML(value: string | typeof nothing | typeof noChange) |
Usable location |
Child expression |
A key security feature of Lit's templating syntax is that only strings originating in Lit's tagged template literals are parsed as HTML. Because these tagged template literals can only be contained in trusted script files, this acts as a natural safeguard against XSS attacks injecting untrusted HTML. All other strings strings, including user controlled content, are treated as plain text and cannot create HTML.
However, there may be cases when HTML text not authored directly in script files
needs to be rendered in a Lit template, for example trusted HTML content fetched
from a database. The unsafeHTML
directive will parse such a string as HTML and
render it in a Lit template expression.
Note, the string passed to unsafeHTML
must be developer-controlled or
sanitized and not include untrusted content. Examples of untrusted content
include query string parameters, values from user inputs, or user-controlled and
unsanitized data. Untrusted content rendered with this directive could lead to
cross-site scripting (XSS)
vulnerabilities.
{% switchable-sample %}
const markup = '<h3>Some HTML to render.</h3>';
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
Look out, potentially unsafe HTML ahead:
${unsafeHTML(markup)}
`;
}
}
const markup = '<h3>Some HTML to render.</h3>';
class MyElement extends LitElement {
render() {
return html`
Look out, potentially unsafe HTML ahead:
${unsafeHTML(markup)}
`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Explore unsafeHTML
more in the playground.
Renders a string as MathML rather than text.
Import |
import {unsafeMathML} from 'lit/directives/unsafe-mathml.js'; |
Signature |
unsafeMathML(value: string | typeof nothing | typeof noChange) |
Usable location |
Child expression |
The unsafeMathML
directive will parse a string as MathML and render it in a
Lit template expression.
Similar to with unsafeHTML
, there may be cases when MathML content
not originating in script files needs to be rendered in a Lit template, for
example trusted MathML content fetched from a database.
Note, the string passed to unsafeMathML
must be developer-controlled or
sanitized and not include untrusted content. Examples of untrusted content
include query string parameters, values from user inputs, or user-controlled and
unsanitized data. Untrusted content rendered with this directive could lead to
cross-site scripting (XSS)
vulnerabilities.
{% switchable-sample %}
const mathmlString = '<mn>1</mn>';
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
Look out, potentially unsafe MathML ahead:
<math>
${unsafeMathML(mathmlString)}
</math> `;
}
}
const mathmlString = '<mn>1</mn>';
class MyElement extends LitElement {
render() {
return html`
Look out, potentially unsafe MathML ahead:
<math>
${unsafeMathML(mathmlString)}
</math> `;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Renders a string as SVG rather than text.
Import |
import {unsafeSVG} from 'lit/directives/unsafe-svg.js'; |
Signature |
unsafeSVG(value: string | typeof nothing | typeof noChange) |
Usable location |
Child expression |
The unsafeSVG
directive will parse a string as SVG and render it in a Lit
template expression.
Similar to with unsafeHTML
, there may be cases when SVG content
not originating in script files needs to be rendered in a Lit template, for
example trusted SVG content fetched from a database.
Note, the string passed to unsafeSVG
must be developer-controlled or
sanitized and not include untrusted content. Examples of untrusted content
include query string parameters, values from user inputs, or user-controlled and
unsanitized data. Untrusted content rendered with this directive could lead to
cross-site scripting (XSS)
vulnerabilities.
{% switchable-sample %}
const svg = '<circle cx="50" cy="50" r="40" fill="red" />';
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
Look out, potentially unsafe SVG ahead:
<svg width="40" height="40" viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg" version="1.1">
${unsafeSVG(svg)}
</svg> `;
}
}
const svg = '<circle cx="50" cy="50" r="40" fill="red" />';
class MyElement extends LitElement {
render() {
return html`
Look out, potentially unsafe SVG ahead:
<svg width="40" height="40" viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg" version="1.1">
${unsafeSVG(svg)}
</svg> `;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Explore unsafeSVG
more in the playground.
Retrieves a reference to an element rendered into the DOM.
Import |
import {ref} from 'lit/directives/ref.js'; |
Signature |
ref(refOrCallback: RefOrCallback) |
Usable location |
Element expression |
Although most DOM manipulation in Lit can be achieved declaratively using templates, advanced situations may required getting a reference to an element rendered in the template and manipulating it imperatively. Common examples of when this may be useful include focusing a form control or calling an imperative DOM manipulation library on a container element.
When placed on an element in the template, the ref
directive will retrieve a
reference to that element once rendered. The element reference may be retrieved
in one of two ways: either by passing a Ref
object or by passing a callback.
A Ref
object acts as a container for a reference to the element, and can be
created using the createRef
helper method found in the ref
module. After
rendering, the Ref
's value
property will be set to the element, where it
can be accessed in post-render lifecycle like updated
.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
inputRef: Ref<HTMLInputElement> = createRef();
render() {
// Passing ref directive a Ref object that will hold the element in .value
return html`<input ${ref(this.inputRef)}>`;
}
firstUpdated() {
const input = this.inputRef.value!;
input.focus();
}
}
class MyElement extends LitElement {
inputRef = createRef();
render() {
// Passing ref directive a Ref object that will hold the element in .value
return html`<input ${ref(this.inputRef)}>`;
}
firstUpdated() {
const input = this.inputRef.value!;
input.focus();
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
A ref callback can also be passed to the ref
directive. The callback will be
called each time the referenced element changes. If a ref callback is
rendered to a different element position or is removed in a subsequent render,
it will first be called with undefined
, followed by another call with the new
element it was rendered to (if any). Note that in a LitElement
, the callback
will be called bound to the host element automatically.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
render() {
// Passing ref directive a change callback
return html`<input ${ref(this.inputChanged)}>`;
}
inputChanged(input?: HTMLInputElement) {
input?.focus();
}
}
class MyElement extends LitElement {
render() {
// Passing ref directive a change callback
return html`<input ${ref(this.inputChanged)}>`;
}
inputChanged(input) {
input?.focus();
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Explore ref
more in the playground.
Renders placeholder content until one or more promises resolve.
Import |
import {until} from 'lit/directives/until.js'; |
Signature |
until(...values: unknown[]) |
Usable location |
Any expression |
Takes a series of values, including Promises. Values are rendered in priority order, with the first argument having the highest priority and the last argument having the lowest priority. If a value is a Promise, a lower-priority value will be rendered until it resolves.
The priority of values can be used to create placeholder content for async data. For example, a Promise with pending content can be the first (highest-priority) argument, and a non-promise loading indicator template can be used as the second (lower-priority) argument. The loading indicator renders immediately, and the primary content will render when the Promise resolves.
{% switchable-sample %}
@customElement('my-element')
class MyElement extends LitElement {
@state()
private content = fetch('./content.txt').then(r => r.text());
render() {
return html`${until(this.content, html`<span>Loading...</span>`)}`;
}
}
class MyElement extends LitElement {
static properties = {
content: {state: true},
};
constructor() {
super();
this.content = fetch('./content.txt').then(r => r.text());
}
render() {
return html`${until(this.content, html`<span>Loading...</span>`)}`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Explore until
more in the playground.
Appends values from an AsyncIterable
into the DOM as they are yielded.
Import |
import {asyncAppend} from 'lit/directives/async-append.js'; |
Signature |
asyncAppend(
iterable: AsyncIterable<I>,
mapper?: (item: I, index?: number) => unknown
) |
Usable location |
Child expression |
asyncAppend
renders the values of an async iterable, appending each new value after the previous. Note that async generators also implement the async iterable protocol, and thus can be consumed by asyncAppend
.
{% switchable-sample %}
async function *tossCoins(count: number) {
for (let i=0; i<count; i++) {
yield Math.random() > 0.5 ? 'Heads' : 'Tails';
await new Promise((r) => setTimeout(r, 1000));
}
}
@customElement('my-element')
class MyElement extends LitElement {
@state()
private tosses = tossCoins(10);
render() {
return html`
<ul>${asyncAppend(this.tosses, (v: string) => html`<li>${v}</li>`)}</ul>`;
}
}
async function *tossCoins(count) {
for (let i=0; i<count; i++) {
yield Math.random() > 0.5 ? 'Heads' : 'Tails';
await new Promise((r) => setTimeout(r, 1000));
}
}
class MyElement extends LitElement {
static properties = {
tosses: {state: true},
};
constructor() {
super();
this.tosses = tossCoins(10);
}
render() {
return html`
<ul>${asyncAppend(this.tosses, (v) => html`<li>${v}</li>`)}</ul>`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Explore asyncAppend
more in the playground.
Renders the latest value from an AsyncIterable
into the DOM as it is yielded.
Import |
import {asyncReplace} from 'lit/directives/async-replace.js'; |
Signature |
asyncReplace(
iterable: AsyncIterable<I>,
mapper?: (item: I, index?: number) => unknown
) |
Usable location |
Any expression |
Similar to asyncAppend
, asyncReplace
renders the values of an async iterable, replacing the previous value with each new value.
{% switchable-sample %}
async function *countDown(count: number) {
while (count > 0) {
yield count--;
await new Promise((r) => setTimeout(r, 1000));
}
}
@customElement('my-element')
class MyElement extends LitElement {
@state()
private timer = countDown(10);
render() {
return html`Timer: <span>${asyncReplace(this.timer)}</span>.`;
}
}
async function *countDown(count) {
while (count > 0) {
yield count--;
await new Promise((r) => setTimeout(r, 1000));
}
}
class MyElement extends LitElement {
static properties = {
timer: {state: true},
};
constructor() {
super();
this.timer = countDown(10);
}
render() {
return html`Timer: <span>${asyncReplace(this.timer)}</span>.`;
}
}
customElements.define('my-element', MyElement);
{% endswitchable-sample %}
Explore asyncReplace
more in the playground.