# 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:
value
—The value of the item in the list. Use this property to access the properties of the array. For example,iteratorname.value.propertyName
.index
—The index of the item in the list.first
—A boolean value indicating whether this item is the first item in the list.last
—A boolean value indicating whether this item is the last item in the list.
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
.
- Statement1 renders if
property1
is true. - Statement2 renders if
property1
is false andproperty2
is true. - Statement3 renders if
property1
andproperty2
are false.
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:
- Use
connectedCallback
on the dynamic component to signal when it's attached to the DOM - Use
renderedCallback
to detect when the dynamic component is attached to the DOM. A dynamic component constructor is guaranteed to be attached to the DOM in the next rendering cycle once it has been set.
<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.