You're viewing docs for an older version of Lit.
For the current version, visit lit.dev.

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:

Where native shadow DOM isn’t available, LitElement uses the Shady CSS polyfill.

Define and render a template

To define a template for a LitElement component, write a render function for your element class:

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
  render() {
    return html`<p>template content</p>`;
  }
}

Example

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {

  // 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.
     */
    return html`
      <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.

Design a performant template

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:

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:

dom-manip.js

// Anti-pattern. Avoid!

constructor() {
  super();
  this.addEventListener('stuff-loaded', (e) => {
    this.shadowRoot.getElementById('message').innerHTML=e.detail;
  });
  this.loadStuff();
}
render() {
  return html`
    <p id="message">Loading</p>
  `;
}

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.

update-properties.js

static get properties() {
  return {
    message: {type: String}
  }
}

constructor() {
  super();
  this.message = 'Loading';
  this.addEventListener('stuff-loaded', (e) => { this.message = e.detail } );
  this.loadStuff();
}
render() {
  return html`
    <p>${this.message}</p>
  `;
}
Code Editor not supported on this browser

The following sections discuss different types of property bindings. See Properties for information on declaring properties.

Use properties, loops, and conditionals in a template

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.

Properties

To add a property value to a template, insert it with ${this.propName}:

static get properties() {
  return {
    myProp: {type: String}
  };
}
...
render() {
  return html`<p>${this.myProp}</p>`;
}

Loops

Iterate over an array:

html`<ul>
  ${this.myArray.map(i => html`<li>${i}</li>`)}
</ul>`;

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.

Conditionals

Render based on a Boolean condition:

html`
  ${this.myBool?
    html`<p>Render some HTML if myBool is true</p>`:
    html`<p>Render some other HTML if myBool is false</p>`}
`;

Examples

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
  static get properties() {
    return {
      myString: { type: String },
      myArray: { type: Array },
      myBool: { type: Boolean }
    };
  }
  constructor() {
    super();
    this.myString = 'Hello World';
    this.myArray = ['an','array','of','test','data'];
    this.myBool = true;
  }
  render() {
    return html`
      <p>${this.myString}</p>
      <ul>
        ${this.myArray.map(i => html`<li>${i}</li>`)}
      </ul>
      ${this.myBool?
        html`<p>Render some HTML if myBool is true</p>`:
        html`<p>Render some other HTML if myBool is false</p>`}
    `;
  }
}

customElements.define('my-element', MyElement);

Code Editor not supported on this browser

Bind properties to templated elements

You can insert JavaScript expressions as placeholders for HTML text content, attributes, Boolean attributes, properties, and event handlers.

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.

Bind to text content

Bind prop1 to text content:

html`<div>${this.prop1}</div>`

Bind to an attribute

Bind prop2 to an attribute:

html`<div id="${this.prop2}"></div>`

Attribute values are always strings, so an attribute binding should return a value that can be converted into a string.

Bind to a boolean attribute

Bind prop3 to a boolean attribute:

html`<input type="text" ?disabled="${this.prop3}">`

Boolean attributes are added if the expression evaluates to a truthy value, and removed if it evaluates to a falsy value.

Bind to a property

Bind prop4 to a property:

html`<input type="checkbox" .value="${this.prop4}"/>`

Bind to an event handler

Bind clickHandler to a click event:

html`<button @click="${this.clickHandler}">pie?</button>`

The default event context for @event expressions is this, so there is no need to bind the handler function.

Examples

my-element.js

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
  static get properties() {
    return {
      prop1: {type: String},
      prop2: {type: String},
      prop3: {type: Boolean},
      prop4: {type: String}
    };
  }
  constructor() {
    super();
    this.prop1 = 'text binding';
    this.prop2 = 'mydiv';
    this.prop3 = true;
    this.prop4 = 'pie';
  }
  render() {
    return html`
      <!-- text binding -->
      <div>${this.prop1}</div>

      <!-- attribute binding -->
      <div id="${this.prop2}">attribute binding</div>

      <!-- boolean attribute binding -->
      <div>
        boolean attribute binding
        <input type="text" ?disabled="${this.prop3}"/>
      </div>

      <!-- property binding -->
      <div>
        property binding
        <input type="text" .value="${this.prop4}"/>
      </div>

      <!-- event handler binding -->
      <div>event handler binding
        <button @click="${this.clickHandler}">click</button>
      </div>
    `;
  }
  clickHandler(e) {
    console.log(e.target);
  }
}

customElements.define('my-element', MyElement);

Code Editor not supported on this browser

Render children with the slot element

Your component may accept children (like a <ul> element can have <li> children).

<my-element>
  <p>A child</p>
</my-element>

By default, if an element has a shadow tree, its children don’t render at all.

To render children, your template needs to include one or more <slot> elements, which act as placeholders for child nodes.

Finding slotted children. If your component needs information about its slotted children, see Accessing slotted children.

Use the slot element

To render an element’s children, create a <slot> for them in the element’s template. For example:

render(){
  return html`
    <div>
      <slot></slot>
    </div>
  `;
}

Children will now render in the <slot>:

<my-element>
  <p>Render me</p>
</my-element>

The children aren’t moved in the DOM tree, but they’re rendered as if they were children of the <slot>.

Arbitrarily many children can populate a single slot:

<my-element>
  <p>Render me</p>
  <p>Me too</p>
  <p>Me three</p>
</my-element>
Code Editor not supported on this browser

Use named slots

To assign a child to a specific slot, ensure that the child’s slot attribute matches the slot’s name attribute:

render(){
  return html`
    <div>
      <slot name="one"></slot>
    </div>
  `;
}

index.html

<my-element>
  <p slot="one">Include me in slot "one".</p>
</my-element>

Examples

my-element.js

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
  render(){
    return html`
      <div>
        <slot name="one"></slot>
        <slot name="two"></slot>
      </div>
    `;
  }
}
customElements.define('my-element', MyElement);

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
  <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  
  <script type="module" src="./my-element.js"></script>
  <title>lit-element code sample</title>
</head>
<body>
    <!-- Assign child to a specific slot -->

    <my-element>
      <p slot="two">Include me in slot "two".</p>
    </my-element>

    <!-- 
      Named slots only accept children with a matching `slot` attribute. 
      
      Children with a `slot` attribute can only go into a slot with a matching name. 
    -->

    <my-element>
      <p slot="one">Include me in slot "one".</p>
      <p slot="nope">This one will not render at all.</p>
      <p>No default slot, so this one won't render either.</p>
    </my-element>
</body>
</html>

Code Editor not supported on this browser

Use name, not id, to select slots.

Note that a slot’s id attribute has no effect!

my-element.js

render(){
  return html`
    <div>
      <slot id="one"></slot>
    </div>
  `;
}

index.html

<my-element>
  <p slot="one">nope.</p>
  <p>ohai..</p>
</my-element>
Code Editor not supported on this browser

Compose a template from other templates

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:

  function headerTemplate(title) {
    return html`<header>${title}</header>`;
  }
  function articleTemplate(text) {
    return html`<article>${text}</article>`;
  }
  function footerTemplate() {
    return html`<footer>Your footer here.</footer>`;
  }

class MyPage extends LitElement {
  ...
  render() {
    return html`
      ${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:

import './my-header.js';
import './my-article.js';
import './my-footer.js';

class MyPage extends LitElement {
  render() {
    return html`
      <my-header></my-header>
      <my-article></my-article>
      <my-footer></my-footer>
    `;
  }
}
Code Editor not supported on this browser

Specify the render root

The node into which your component’s template will render is called its render root.

By default, LitElement creates an open shadowRoot and renders inside it, producing the following DOM structure:

<my-element>
  #shadow-root
    <p>child 1</p>
    <p>child 2</p>

To customize a component’s render root, implement createRenderRoot and return the node you want the template to render into.

For example, to render the template into the main DOM tree as your element’s children:

<my-element>
  <p>child 1</p>
  <p>child 2</p>

Implement createRenderRoot and return this:

class LightDom extends LitElement {
  render() {
    return html`
      <p>This template renders without shadow DOM.</p>
    `;
  }
  createRenderRoot() {
  /**
   * Render template without shadow DOM. Note that shadow DOM features like
   * encapsulated CSS and slots are unavailable.
   */
    return this;
  }
}
Code Editor not supported on this browser

Template syntax cheat sheet

Render

render() { return html`<p>template</p>`; }

Properties, loops, conditionals

// Property
html`<p>${this.myProp}</p>`;

// Loop
html`${this.myArray.map(i => html`<li>${i}</li>`)}`;

// Conditional
html`${this.myBool?html`<p>foo</p>`:html`<p>bar</p>`}`;

Data bindings

// Attribute
html`<p id="${...}">`;

// Boolean attribute
html`<input type="text" ?disabled="${...}">`;

// Property
html`<input .value="${...}">`;

// Event handler
html`<button @click="${this.doStuff}"></button>`;

Composition

// From multiple templates on same class

render() {
  return html`
    ${this.headerTemplate}
    <article>article</article>
  `;
}
get headerTemplate() {
  return html`<header>header</header>`;
}
// By importing elements
import './my-header.js';

class MyPage extends LitElement{
  render() {
    return html`
      <my-header></my-header>
      <article>article</article>
    `;
  }
}

Slots

render() { return html`<slot name="thing"></slot>`; }
<my-element>
  <p slot="thing">stuff</p>
</my-element>

Using other lit-html features

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:

npm i lit-element@^2.0.0
npm i lit-html@^1.0.0

Import and use a lit-html directive

You can import and use a lit-html directive and use it as shown in the lit-html documentation.

import { LitElement, html } from 'lit-element';
import { until } from 'lit-html/directives/until.js';

const content = fetch('./content.txt').then(r => r.text());

html`${until(content, html`<span>Loading...</span>`)}`

For a list of directives supplied with lit-html, see Built-in directives in the Template syntax reference.

Accessing nodes in the shadow DOM

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:

get _closeButton() {
  return this.shadowRoot.querySelector('#close-button');
}

LitElement supplies a set of decorators that provide a shorthand way of defining getters like this.

More information:

@query, @queryAll, and @queryAsync decorators

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.

import {LitElement, html} from 'lit-element';
import {query} from 'lit-element/lib/decorators.js';

class MyElement extends LitElement {
  @query('#first')
  _first;

  render() {
    return html`
      <div id="first"></div>
      <div id="second"></div>
    `;
  }
}

This decorator is equivalent to:

get first() {
  return this.renderRoot.querySelector('#first');
}

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.

import {LitElement, html} from 'lit-element';
import {queryAll} from 'lit-element/lib/decorators.js';

class MyElement extends LitElement {
  @queryAll('div')
  _divs;

  render() {
    return html`
      <div id="first"></div>
      <div id="second"></div>
    `;
  }
}

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.

Accessing slotted children

To access children assigned to slots in your shadow root, you can use the standard slot.assignedNodes method and the slotchange event.

For example, you can create a getter to access assigned nodes for a particular slot:

get _slottedChildren() {
  const slot = this.shadowRoot.querySelector('slot');
  const childNodes = slot.assignedNodes({flatten: true});
  return Array.prototype.filter.call(childNodes, (node) => node.nodeType == Node.ELEMENT_NODE);
}

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.

handleSlotchange(e) {
  const childNodes = e.target.assignedNodes({flatten: true});
  // ... do something with childNodes ...
  this.allText = Array.prototype.map.call(childNodes, (node) => {
    return node.textContent ? node.textContent : ''
  }).join('');
}

render() {
  return html`<slot @slotchange=${this.handleSlotchange}></slot>`;
}

More information:

@queryAssignedNodes decorator

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:

get headerNodes() {
  const slot = this.shadowRoot.querySelector('slot[name=header]');
  return slot.assignedNodes({flatten: true});
}

For TypeScript, the typing of a queryAssignedNodes property is NodeListOf<HTMLElement>.

Resources

For more information on shadow DOM:

For more information on lit-html templates: