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.
Render static HTML
Permalink to “Render static 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.
Render dynamic text content
Permalink to “Render dynamic text content”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.
Using expressions
Permalink to “Using expressions”The previous example shows interpolating a simple text value, but the binding can include any kind of JavaScript expression:
Bind to attributes
Permalink to “Bind to attributes”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:
Bind to properties
Permalink to “Bind to properties”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.
Add event listeners
Permalink to “Add event listeners”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).
Nest and compose templates
Permalink to “Nest and compose templates”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.
Conditional templates
Permalink to “Conditional templates”lit-html has no built-in control-flow constructs. Instead you use normal JavaScript expressions and statements.
Conditionals with ternary operators
Permalink to “Conditionals with ternary operators”Ternary expressions are a great way to add inline conditionals:
Conditionals with if statements
Permalink to “Conditionals with if statements”You can express conditional logic with if statements outside of a template to compute values to use inside of the template:
Repeating templates
Permalink to “Repeating templates”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.
Repeating templates with Array.map
Permalink to “Repeating templates with Array.map”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 aTemplateResult
.
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
, therepeat
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.
Rendering nothing
Permalink to “Rendering nothing”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.
nothing and the slot fallback content
Permalink to “nothing and the slot fallback content”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.