# HTML Templates

A component that renders UI has an HTML template file and a JavaScript class file, which is an ES module.

The power of Lightning Web Components is the templating system, which uses the virtual DOM to render components smartly and efficiently. Use simple syntax to declaratively bind a component’s template to data in the component’s JavaScript class.

A template is valid HTML with a <template> root tag. Write templates using standard HTML and a few directives that are unique to Lightning Web Components. Directives are special HTML attributes, like for:each and lwc:if, that give you more power to manipulate the DOM in markup.

Let Lightning Web Components manipulate the DOM instead of writing JavaScript to do it.

Note

A template can include nested <template> tags with certain directives. Only for:each, iterator:iteratorname, and lwc:if|elseif|else are supported in nested <template> tags.

# Data Binding

To bind a property in a component’s template to a property in the component’s JavaScript class, in the template, surround the property with curly braces: {property}.

Here’s the simplest example of data binding. The name property in the template is bound to the name property in the JavaScript class.

In helloWorld.js, change the value of World! and you'll see the component update.

To see the power of data binding, add another property in helloWorld.js.

@api announcement = 'I\'m coding web components.'; 

In helloWorld.html, add {announcement}.

The property in { } must be a valid JavaScript identifier or member expression. For example, {data} and {data.name} are both valid. Don’t add spaces around the property, for example, { data } is not valid HTML. It's illegal to compute a value in an expression, like data[2].name['John']. To compute a value, use a getter.

The @api decorator makes the name property public so that other components can set it. If we remove @api, the property still binds to the HTML template, but it's private. To see for yourself, remove @api in the playground. When the value of name changes, the component re-renders.

This code is standard HTML and JavaScript. The only feature in this code that’s exclusive to Lightning Web Components is the @api decorator.

Important

Before we go further, it's important to understand naming. A component's folder and file names are camel case, helloWorld, helloWorld.html, and helloWorld.css. In HTML markup, camelCase maps to kebab-case. When a component renders, the <template> tag is replaced with <namespace-component-name>. The tag includes the component's namespace. For example, a component with the template helloWorld.html in the example namespace renders as <example-hello-world>. For more information about naming, see Component Bundles.

# Handle User Input

This component has an input field that asks for a name to greet. When the value of the input field changes, the component changes the value of the bound name property.

Type something in the input field to see the recipe in action.

The <input> element uses the oninput event to listen for a change to its value. To bind the handleInput function to the template, we use the same syntax that we use to bind a property, {...}. The handleInput JavaScript function executes when the value of the <input> element changes.

When the value of the name property changes, the component re-renders.

We talk about event handling in-depth in another topic, but notice in helloBinding.js that handleInput is passed an event object. The event object contains information about the input event. The component uses the event object to get the value that the user enters into the input field.

Note

In LWC versions 1.1.0 and higher, properties that contain a primitive value are reactive. If a reactive property is used in a template and its value changes, the component re-renders.

# Use Getters to Compute a Value

To dynamically compute a value for a property used in a template, define a JavaScript getter that computes the value.

<template> {propertyName} </template> 
import { LightningElement } from 'lwc'; export default class Component extends LightningElement { get propertyName() { // Compute a value for propertyName } } 

Getters are much more powerful than computing values in template expressions because they’re JavaScript functions and can contain any code that's legal in a function. Getters also enable unit testing, which reduces bugs and the pain of debugging.

When a component renders, all the expressions used in the template are evaluated, which means that getters are invoked.

This example takes two variables, firstName and lastName, concatenates them, and converts them to uppercase.

# Render Lists

To render a list of items, use for:each directive or the iterator directive to iterate over an array.

The iterator directive has first and last properties that let you apply special behaviors to the first and last items in an array.

Regardless of which directive you use, you must use a key directive to assign a unique ID to each item. When a list changes, the framework uses the key to re-render only the item that changed. The key in the template is used for performance optimization and isn’t reflected in the DOM at runtime.

This code you write to render lists is standard HTML and JavaScript, except for the directives, which are exclusive to Lightning Web Components.

# for:each

To render an array, add the for:each={array} directive to a nested <template> tag that encloses the HTML elements you want to repeat. To access the current item, use for:item="currentItem". To access the current item’s index, use for:index="index".

Every item in a list must have a key. To assign a key to every element in the list, use the key={uniqueId} directive.

This example iterates over an array called contacts, which is defined in the component’s JavaScript class.

Important

Every item in a list must have a key. When a list changes, the framework uses the key to identify each item so that it can re-render only the item that changed. The key must be a string or a number, it can't be an object. You can’t use index as a value for key. Assign unique keys to an incoming data set. To add new items to a data set, use a private property to track and generate keys. Use this code pattern.

# iterator

To apply a special behavior to the first or last item in a list, use the iterator directive, iterator:iteratorname={array}. Add the iterator directive to a nested <template> tag that encloses the HTML elements you want to repeat. iteratorname must be in lowercase.

Use iteratorname to access these properties:

This sample code uses the same array as the previous example. To apply special rendering to the first and last items in the list, the code uses the first and last properties with the if:true directive.

If the item is first in the list, the <div> tag renders with the styling defined in the CSS list-first class. If the item is last in the list, the <div> tag renders with the styling defined in the CSS list-last class.

Important

To access the value of a property in the array, use iteratorname.value.propertyName, not iteratorname.propertyName. For example, in helloIterator.html, look at {it.value.Name} and {it.value.Title}.

# Render HTML Conditionally

To render HTML conditionally, add the lwc:if|elseif={property} and lwc:else directives to a <template> tag that encloses the conditional content.

The lwc:if|elseif directives bind property to the template, removing and inserting DOM elements based on whether property is a truthy or falsy value.

This example component has properties called property1 and property2.

<template> <template lwc:if={property1}> statement1 </template> <template lwc:elseif={property2}> statement2 </template> <template lwc:else> statement3 </template> </template> 

Only one of the statements renders depending on the values of property1 and property2.

Note

We recommend that you use the lwc:if, lwc:elseif, and lwc:else directives instead of the legacy if:true and if:false directives. The lwc:if, lwc:elseif, and lwc:else directives are more performant and are similar to if-statements in JavaScript. if:true and if:false are no longer recommended and they may be removed in the future.

This example component has a property called isTemplateOne. The component has two templates. Only one renders depending on the value of isTemplateOne.

<!-- helloConditionalRendering.html --> <template> <template lwc:if={isTemplateOne}> This is template one. </template> <template lwc:else> This is not template one. This is template two. </template> 

To change which template renders, change the value of isTemplateOne to true in the component's JavaScript class.

Notice that the JavaScript doesn’t manipulate the DOM, it simply changes the value of a property.

# Render Multiple Templates

You may want to render a component with more than one look and feel, but not want to mix the HTML in one file. For example, one version of the component is plain, and another version displays an image and extra text. In this case, you can import multiple HTML templates and write business logic that renders them conditionally. This pattern is similar to the code splitting used in some JavaScript frameworks.

Create multiple HTML files in the component bundle. Import them all and add a condition in the render() method to return the correct template depending on the component’s state.

The returned value from the render() method must be a template reference, which is the imported default export from an HTML file. In this example, the template references are templateOne and templateTwo.

To reference CSS from an extra template, the CSS filename must match the filename of the extra template. For example, the template one.html can reference CSS only from one.css. It can’t reference CSS from multi.css.

Note

Because of subtleties in how CSS is applied and the CSS cascade, styles from one template may not completely replace styles from the other template. It is recommended to use scoped styles if you want the styles for one template to only apply to that template. This is recommended for both light DOM and shadow DOM.

Here are the files used in the example:

multi ├──multi.js ├──multi.html ├──multi.css ├──one.html ├──one.scoped.css ├──two.html └──two.scoped.css 

Note

It's rare to define render() in a component. It's more common to use an if:true|false directive to render nested templates conditionally.

# Inject Raw HTML Content

Apps dealing with rich content sometimes need to render the content as raw HTML, such as for content coming from a database or CMS.

To inject HTML content from JavaScript, use the lwc:inner-html directive.

Note

This lwc:inner-html directive is not available to external developers building Lightning web components on the Salesforce Platform.

example.js

import { LightningElement } from 'lwc'; export default class Example extends HTMLElement { content = 'Hello <strong>world</strong>!'; } 

example.html

<template> <div lwc:inner-html={content}></div> </template> 

The output looks like this.

<x-example> # shadow-root | <div> | Hello <strong>world</strong>! | </div> </x-example> 

Currently, injecting raw HTML content directly into the DOM is possible using the renderedCallback() lifecycle hook, which doesn't work for server-side rendered components. The lwc:inner-html directive enables you to inject raw HTML content that works in the browser and on the server.

# Sanitize HTML Using sanitizeHtmlContent()

The lwc:inner-html directive binds the content to the Element.innerHTML property. To prevent malicious content injection, use the sanitizeHtmlContent(content) method.

sanitizeHtmlContent(content)

Removes malicious code from the content to be injected into the DOM. By default, this method throws an error to indicate that it needs to be overridden.

sanitizeHtmlContent() takes in the content parameter, which is the value passed to the lwc:inner-html directive.

This method returns a string containing the sanitized HTML content.

# Override the sanitizeHtmlContent(content) Method

Call setHooks() to override the sanitizeHtmlContent() method. This example assumes you are using the DOMPurify library.

// main.js import { createElement } from "lwc"; import App from "x/app"; import DOMPurify from 'dompurify'; setHooks({ sanitizeHtmlContent(content) { return DOMPurify.sanitize(content); } }); const elm = createElement("x-app", { is: App }); document.body.appendChild(elm); 

In your app template, use the lwc:inner-html directive.

<!-- app.html --> <template> <div lwc:inner-html={content}> </div> 

Import the setHooks module in your JavaScript code.

import { LightningElement } from 'lwc'; import { setHooks } from 'lwc'; export default class App extends LightningElement { content='<x-cmp></x-cmp>'; } 

# Injecting Content in Shadow DOM

When your components run in native shadow, the injected content gets the shadow DOM style automatically. In synthetic shadow, lwc:inner-html functions similarly to lwc:dom="manual", applying the scoped styles to the manually injected content.

In native shadow, content injected via lwc:inner-html containing slot elements might conflict with existing slots that are defined in the template. In synthetic shadow, LWC transforms the slot elements in the template. Content injected via lwc:inner-html containing slot elements doesn't participate in the slotting.

# Import and Instantiate a Component Dynamically

A dynamic component is a custom element whose constructor is only known at runtime. For enablement information, see the <lwc:component> reference.

Note

Using lwc:dynamic to create a component dynamically is deprecated and no longer recommended.

To create a component dynamically, use the <lwc:component> managed element with the lwc:is directive.

<template> <div class="container"> <lwc:component lwc:is={componentConstructor}></lwc:component> </div> </template> 

Import the custom element in the connectedCallback() method using the import() dynamic import syntax.

import { LightningElement } from 'lwc'; export default class extends LightningElement { componentConstructor; // Use connectedCallback() on the dynamic component // to signal when it's attached to the DOM connectedCallback() { import('c/concreteComponent') .then(({ default: ctor }) => this.componentConstructor = ctor) .catch(err => console.log('Error importing component')); } } 

The import() call returns a promise that resolves to a LightningElement constructor. The element is then rendered in place of lwc:component. The tag name used for the dynamic component is the value that's returned for the given constructor.

Similar to a regular LWC custom element, the dynamic component is created and attached to the DOM. If the dynamic component's constructor changes, the existing element is removed from the DOM.

In this example, the following HTML is rendered after the import completes.

<div class="container"> <c-concrete-component></c-concrete-component> </div> 

# Dynamic Imports

As shown in the previous example, dynamic component creation involves making an import() call. Dynamic imports enable a component to be lazy loaded, which facilitates the import of the component constructor at runtime.

<template> <lwc:component lwc:is={componentConstructor}></lwc:component> </template> 

To assign the component constructor as the default import, you can also rename the default key. Use the async and await operators to return the component constructor.

import { LightningElement } from 'lwc'; export default class App extends LightningElement { componentConstructor; async connectedCallback() { const { default: ctor } = await import('c/myComponent'); this.componentConstructor = ctor; } } 

# Select the Dynamic Component

The custom element must be attached to the DOM before you can select it. To select the dynamic component, use the lwc:ref directive or use an attribute that's assigned to the component, such as using a class name.

To identify if a dynamic component is attached to the DOM:

<template> <lwc:component lwc:is={componentConstructor} lwc:ref="myCmp"></lwc:component> </template> 
import { LightningElement } from 'lwc'; export default class extends LightningElement { componentConstructor; connectedCallback() { import('lightning/concreteComponent') .then(({ default: ctor }) => this.componentConstructor = ctor) .catch(err => console.log('Error importing component')); } renderedCallback() { // this.refs.myCmp will be available on the next rendering cycle after the constructor has been set if (this.refs.myCmp) { // this.refs.myCmp will contain a reference to the DOM node console.log(this.refs.myCmp); } } } 

# Assign Attributes on a Dynamic Component

To pass attributes to lwc:component, assign them directly to the tag or imperatively in JavaScript.

<template> <lwc:component lwc:is={ctor} class="container"></lwc:component> </template> 

Assign attributes imperatively in JavaScript using the renderedCallback() method.

<template> <lwc:component lwc:is={componentConstructor} lwc:ref="foo"></lwc:component> </template> 
import { LightningElement } from "lwc"; export default class extends LightningElement { componentConstructor; connectedCallback() { import('lightning/concreteComponent') .then(({ default: ctor }) => this.componentConstructor = ctor) .catch(err => console.log('Error importing component')); } renderedCallback() { if (this.refs.foo) { this.refs.foo.setAttribute('title', 'hawaii'); } } } 

# Dynamic Component's Children Elements

You can include children elements on the dynamic component. <lwc:component> first creates the dynamic component and then its children. Each time the dynamic component changes, the existing element is removed from the DOM along with all of its children. The new dynamic component is then created along with its children.

<template> <lwc:component lwc:is={ctor}> <span>child</span> </lwc:component> </template> 

# Attach Event Listeners and Pass Properties to Dynamic Components

In most cases, you want to set the properties and attach the event listeners based on which component is dynamically loaded. By using lwc:component with lwc:is, you can load components dynamically. Additionally, use the lwc:spread directive to set which properties to pass. Use lwc:on to dynamically set which event listeners to attach to the components.

The example shows how you can use all four directives to load components dynamically while passing properties to them and attaching event handlers for them.

The dynamic component defines the properties and an event handler, passing it to the childA and childB components. When the button on dynamic is clicked, it creates either the childA or childB component dynamically with the passed in properties and event handler.

# Render Dynamic Components in Light DOM or Shadow DOM

A dynamic component can switch between light DOM and shadow DOM. Let's say you are alternating between the two DOM models.

<template> <template lwc:if={renderLight}> <light-component></light-component> </template> <template lwc:else> <shadow-component></shadow-component> </template> </template> 

Using the lwc:if and lwc:else conditional directives is semantically the same as the following dynamic component.

<template> <!-- The ctor swaps between light and shadow DOM components --> <lwc:component lwc:is={ctor}></lwc:component> </template> 

We recommend using scoped styles or shadow DOM to work with component styling. Light DOM styles are injected to the closest root node and are not removed once the components are no longer available in the DOM. Repeated insertion and removal of dynamic components in light DOM can cause styles to be overwritten.