You're viewing docs for an older version of Lit. Click here for the latest version.
Templates
Add a template to your component to define internal DOM to implement your component.
To encapsulate the templated DOM LitElement uses shadow DOM. Shadow DOM provides three benefits:
DOM scoping. DOM APIs like document.querySelector won't find elements in the component's shadow DOM, so it's harder for global scripts to accidentally break your component.
Style scoping. You can write encapsulated styles for your shadow DOM that don't affect the rest of the DOM tree.
Composition. The component's shadow DOM (managed by the component) is separate from the component's children. You can choose how children are rendered in your templated DOM. Component users can add and remove children using standard DOM APIs without accidentally breaking anything in your shadow DOM.
Where native shadow DOM isn't available, LitElement uses the Shady CSS polyfill.
To define a template for a LitElement component, write a render function for your element class:
import { LitElement, html } from'lit-element';
classMyElementextendsLitElement {
render() {
returnhtml`<p>template content</p>`;
}
}
Write your template in HTML inside a JavaScript template literal by enclosing the raw HTML in back-ticks (``).
Tag your template literal with the html tag function.
The component's render method can return anything that lit-html can render. Typically, it returns a single TemplateResult object (the same type returned by the html tag function).
Example
import { LitElement, html } from'lit-element';
classMyElementextendsLitElement {
// Implement `render` to define a template for your element.
render(){
/**
* Return a lit-html `TemplateResult`.
*
* To create a `TemplateResult`, tag a JavaScript template literal
* with the `html` helper function.
*/
returnhtml`
<div>
<p>A paragraph</p>
</div>
`;
}
}
customElements.define('my-element', MyElement);
Code Editor not supported on this browser
LitElement uses lit-html templates; this page summarizes the features of lit-html templates, for more details, see Writing templates and the Template syntax reference in the lit-html documentation.
LitElement renders and re-renders asynchronously, updating in response to batched property changes (see Element update lifecycle for more information).
During an update, only the parts of the DOM that change are re-rendered. To get the performance benefits of this model, you should design your element's template as a pure function of its properties.
To do this, make sure the render function:
Does not change the element's state.
Does not have any side effects.
Only depends on the element's properties.
Returns the same result when given the same property values.
Also, avoid making DOM updates outside of render. Instead, express the element's template as a function of its state, and capture its state in properties.
The following code uses inefficient DOM manipulation:
We can improve the template by declaring the message as a property, and binding the property into the template. Declaring a property tells your component to re-render its template when the property changes.
When defining your element's template, you can bind the element's properties to the template; the template is re-rendered whenever the properties change.
repeat directive. In most cases, Array.map is the most efficient way to create a repeating template. In some cases, you may want to consider lit-html's repeat directive. In particular, if the repeated elements are stateful, or very expensive to regenerate. For more information, see Repeating templates with the repeat directive in the lit-html docs.
You can insert JavaScript expressions as placeholders for HTML text content, attributes, Boolean attributes, properties, and event handlers.
Text content: <p>${...}</p>
Attribute: <p id="${...}"></p>
Boolean attribute: ?disabled="${...}"
Property: .value="${...}"
Event handler: @event="${...}"
JavaScript expressions can include your element's properties. LitElement observes and reacts to property changes, so your templates update automatically.
Data bindings are always one-way (parent to child). To share data from a child element to its parent, fire an event and capture the relevant data in the detail property.
You can compose LitElement templates from other LitElement templates. In the following example, we compose a template for an element called <my-page> from smaller templates for the page's header, footer, and main content:
functionheaderTemplate(title) {
returnhtml`<header>${title}</header>`;
}
functionarticleTemplate(text) {
returnhtml`<article>${text}</article>`;
}
functionfooterTemplate() {
returnhtml`<footer>Your footer here.</footer>`;
}
classMyPageextendsLitElement {
...
render() {
returnhtml`
${headerTemplate(this.article.title)}
${articleTemplate(this.article.text)}
${footerTemplate()}
`;
}
}
Code Editor not supported on this browser
You can also compose templates by importing other elements and using them in your template:
Since LitElement uses the lit-html html tag function to define templates you can take advantage of the entire lit-html feature set for writing your templates. This includes lit-html directives, special functions that customize the way lit-html renders a binding.
To import features directly from lit-html, your project should add lit-html as a direct dependency. We recommend using the widest practical version range for lit-html, to minimize the chance of npm installing two different versions of lit-html:
The render() method result is usually rendered into shadow DOM, so the nodes are not direct children of the component. Use this.shadowRoot.querySelector() or this.shadowRoot.querySelectorAll() to find nodes in the shadow DOM.
You can query the templated DOM after its initial render (for example, in firstUpdated), or use a getter pattern, like this:
The @query, @queryAll, and @queryAsync decorators all provide a convenient way to access nodes in the component's shadow root.
Using decorators. Decorators are a proposed JavaScript feature, so you’ll need to use a compiler like Babel or TypeScript to use decorators. See Using decorators for details.
The @query decorator modifies a class property, turning it into a getter that returns a node from the render root. The optional second argument is a cache flag which when true performs the DOM query only once and caches the result. This can be used as a performance optimization in cases when the node being queried is not expected to change.
shadowRoot and renderRoot. The renderRoot property identifies the container that the template is rendered into. By default, this is the component's shadowRoot. The decorators use renderRoot, so they should work correctly even if you override createRenderRoot as described in Specify the render root
The @queryAll decorator is identical to query except that it returns all matching nodes, instead of a single node. It's the equivalent of calling querySelectorAll.
Here, divs would return both <div> elements in the template. For TypeScript, the typing of a @queryAll property is NodeListOf<HTMLElement>. If you know exactly what kind of nodes you'll retrieve, the typing can be more specific:
@queryAll('button')
_buttons!: NodeListOf<HTMLButtonElement>
The exclamation point (!) after buttons is TypeScript's non-null assertion operator. It tells the compiler to treat buttons as always being defined, never null or undefined.
Finally, @queryAsync works like @query, except that instead of returning a node directly, it returns a Promise that resolves to that node. Code can use this instead of waiting for the updateComplete promise.
This is useful, for example, if the node returned by @queryAsync can change as a result of another property change.
You can also use the slotchange event to take action when the assigned nodes change. The following example extracts the text content of all of the slotted children.
The @queryAssignedNodes decorator converts a class property into a getter that returns all of the assigned nodes for a given slot in the component's shadow tree. The optional second boolean argument when true flattens the assigned nodes, meaning any assigned nodes that are slot elements are replaced with their assigned nodes. The optional third argument is a css selector which filters the results to matching elements.
Using decorators. Decorators are a proposed JavaScript feature, so you’ll need to use a compiler like Babel or TypeScript to use decorators. See Using decorators for details.
// First argument is the slot name
// Second argument is `true` to flatten the assigned nodes.
@queryAssignedNodes('header', true)
_headerNodes;
// If the first argument is absent or an empty string, list nodes for the default slot.
@queryAssignedNodes()
_defaultSlotNodes;
The first example above is equivalent to the following code: