You're viewing docs for an older version of Lit. Click here for the latest version.

Writing templates

lit-html is a templating library that provides fast, efficient rendering and updating of HTML. It lets you express web UI as a function of data.

This section introduces the main features and concepts in lit-html.

The simplest thing to do in lit-html is to render some static HTML.

The lit-html template is a tagged template literal. The template itself looks like a regular JavaScript string, but enclosed in backticks (`) instead of quotes. The browser passes the string to lit-html's html tag function.

The html tag function returns a TemplateResult—a lightweight object that represents the template to be rendered.

The render function actually creates DOM nodes and appends them to a DOM tree. In this case, the rendered DOM replaces the contents of the document's body tag.

You can't get very far with a static template. lit-html lets you create bindings using ${expression} placeholders in the template literal:

To make your template dynamic, you can create a template function. Call the template function any time your data changes.

When you call the template function, lit-html captures the current expression values. The template function doesn't create any DOM nodes, so it's fast and cheap.

The template function returns a TemplateResult that's a function of the input data. This is one of the main principles behind using lit-html: creating UI as a function of state.

When you call render, lit-html only updates the parts of the template that have changed since the last render. This makes lit-html updates very fast.

The previous example shows interpolating a simple text value, but the binding can include any kind of JavaScript expression:

In addition to using expressions in the text content of a node, you can bind them to a node's attribute and property values, too.

By default, an expression in the value of an attribute creates an attribute binding:

Since attribute values are always strings, the expression should return a value that can be converted into a string.

Use the ? prefix for a boolean attribute binding. The attribute is added if the expression evaluates to a truthy value, removed if it evaluates to a falsy value:

You can also bind to a node's JavaScript properties using the . prefix and the property name:

You can use property bindings to pass complex data down the tree to subcomponents. For example, if you have a my-list component with a listItems property, you could pass it an array of objects:

Note that the property name in this example—listItems—is mixed case. Although HTML attributes are case-insensitive, lit-html preserves the case when it processes the template.

Templates can also include declarative event listeners. An event listener looks like an attribute binding, but with the prefix @ followed by an event name:

This is equivalent to calling addEventListener('click', clickHandler) on the button element.

The event listener can be either a plain function, or an object with a handleEvent method:

Event listener objects. When you specify a listener using an event listener object, the listener object itself is set as the event context (this value).

You can also compose templates to create more complex templates. When a binding in the text content of a template returns a TemplateResult, the TemplateResult is interpolated in place.

You can use any expression that returns a TemplateResult, like another template function:

Composing templates opens a number of possibilities, including conditional and repeating templates.

lit-html has no built-in control-flow constructs. Instead you use normal JavaScript expressions and statements.

Ternary expressions are a great way to add inline conditionals:

You can express conditional logic with if statements outside of a template to compute values to use inside of the template:

You can use standard JavaScript constructs to create repeating templates.

lit-html also provides some special functions, called directives, for use in templates. You can use the repeat directive to build certain kinds of dynamic lists more efficiently.

To render lists, you can use Array.map to transform a list of data into a list of templates:

Note that this expression returns an array of TemplateResult objects. lit-html will render an array or iterable of subtemplates and other values.

Repeating templates with looping statements

Permalink to “Repeating templates with looping statements”

You can also build an array of templates and pass it into a template binding.

Repeating templates with the repeat directive

Permalink to “Repeating templates with the repeat directive”

In most cases, using loops or Array.map is an efficient way to build repeating templates. However, if you want to reorder a large list, or mutate it by adding and removing individual entries, this approach can involve recreating a large number of DOM nodes.

The repeat directive can help here. Directives are special functions that provide extra control over rendering. lit-html comes with some built-in directives like repeat.

The repeat directive performs efficient updates of lists based on user-supplied keys:

repeat(items, keyFunction, itemTemplate)

Where:

  • items is an Array or iterable.
  • keyFunction is a function that takes a single item as an argument and returns a guaranteed unique key for that item.
  • itemTemplate is a template function that takes the item and its current index as arguments, and returns a TemplateResult.

For example:

If you re-sort the employees array, the repeat directive reorders the existing DOM nodes.

To compare this to lit-html's default handling for lists, consider reversing a large list of names:

  • For a list created using Array.map, lit-html maintains the DOM nodes for the list items, but reassigns the values.
  • For a list created using repeat, the repeat directive reorders the existing DOM nodes, so the nodes representing the first list item move to the last position.

Which repeat is more efficient depends on your use case: if updating the DOM nodes is more expensive than moving them, use the repeat directive. Otherwise, use Array.map or looping statements.

Sometimes, you may want to render nothing at all. The values undefined, null and the empty string ('') in a text binding all render an empty text node. In most cases, that's exactly what you want:

In some cases, you want to render nothing at all. In these cases, you can use the nothing value provided by lit-html.

In this case, when user.isAdmin is false, no text node is rendered.

One specific use case where an empty text node causes issues is when you're using a <slot> element inside a shadow root.

This use case is very specific to shadow DOM, and you probably won't encounter it unless you're using lit-html as part of LitElement or another web components base class.

Imagine you have a custom element, example-element, that has a slot in its shadow DOM:

The slot defines fallback content for when there is no content defined to be put in the slot.

So, extending the previous example:

If the user is logged in, the Delete button is rendered. If the user is not logged in, nothing is rendered inside of example-element. This means the slot is empty and its fallback content "Sorry, no content available. I am just fallback content" is rendered.

Replacing nothing in this example with the empty string causes an empty text node to be rendered inside example-element, suppressing the fallback content.

Whitespace creates text nodes. For the example to work, the text binding inside <example-element> must be the entire contents of <example-element>. Any whitespace outside of the binding delimiters adds static text nodes to the template, suppressing the fallback content. However, whitespace inside the binding delimiters is fine.

The two following examples show an element with extra whitespace surrounding the binding delimiters.

Caching template results: the cache directive

Permalink to “Caching template results: the cache directive”

In most cases, JavaScript conditionals are all you need for conditional templates. However, if you're switching between large, complicated templates, you might want to save the cost of recreating DOM on each switch.

In this case, you can use the cache directive. Directives are special functions that provide extra control over rendering. The cache directive caches DOM for templates that aren't being rendered currently.

When lit-html re-renders a template, it only updates the modified portions: it doesn't create or remove any more DOM than it needs to. But when you switch from one template to another, lit-html needs to remove the old DOM and render a new DOM tree.

The cache directive caches the generated DOM for a given binding and input template. In the example above, it would cache the DOM for both the summaryView and detailView templates. When you switch from one view to another, lit-html just needs to swap in the cached version of the new view, and update it with the latest data.