Lit Templates
This lesson explains Lit templates and how to use the html tagged template literal to render efficient, declarative UI inside Lit Web Components.
What Are Lit Templates?
Lit templates are HTML markup written with the
html tagged template literal. They let you define
component UI declaratively, bind dynamic values, and return a
TemplateResult from the render() method.
Lit efficiently updates only the parts of the DOM that change,
which makes templates a core feature of building fast Lit
components.
| Concept | Description |
html`...` | Tagged template for markup |
render() | Method that returns the component UI |
TemplateResult | Renderable Lit template output |
| Bindings | Dynamic values inside ${ } |
| Reactivity | Lit re-renders when properties change |
| Main Benefit | Declarative UI with efficient DOM updates |
Basic Lit Template Example
import { LitElement, html, css } from "lit";
import { customElement, property } from "lit/decorators.js";
@customElement("greeting-card")
class GreetingCard extends LitElement {
@property()
name = "Guest";
render() {
return html`
<article class="card">
<h2>Hello, ${this.name}!</h2>
<p>Welcome to Lit templates.</p>
</article>
`;
}
}
The render() method returns an
html template. When name changes, Lit
updates the rendered output efficiently.
How Lit Templates Work
| Step | What Happens |
| 1. Define template | Write markup with html`...` |
| 2. Bind values | Insert dynamic data with ${ } |
| 3. Return from render() | Lit converts it to a TemplateResult |
| 4. Property changes | Lit schedules a re-render |
| 5. DOM update | Only changed parts of the template update |
Template Bindings
render() {
return html`
<section>
<h2>${this.title}</h2>
<p>${this.description}</p>
<button
type="button"
?disabled="${this.loading}"
@click="${this.handleClick}"
>
${this.buttonLabel}
</button>
</section>
`;
}
| Syntax | Purpose |
${value} | Text, attribute, or property binding |
?attr="${bool}" | Boolean attribute binding |
.prop="${value}" | Property binding |
@event="${handler}" | Event listener binding |
Conditional Rendering
render() {
return html`
<section>
${this.loading
? html`<p>Loading...</p>`
: html`<p>${this.message}</p>`
}
</section>
`;
}
Use JavaScript conditionals inside templates to render different
markup based on component state.
Rendering Lists
render() {
return html`
<ul>
${this.items.map(
(item) => html`
<li>${item.label}</li>
`
)}
</ul>
`;
}
Map arrays to nested html templates to render lists
of items dynamically.
Combining Templates and Styles
static styles = css`
:host {
display: block;
}
.card {
padding: 1rem;
border: 1px solid #dbe5f1;
border-radius: 0.75rem;
}
`;
render() {
return html`
<article class="card">
<slot></slot>
</article>
`;
}
Lit components usually combine static styles with
render() templates inside Shadow DOM.
Slots in Lit Templates
render() {
return html`
<article class="card">
<header>
<slot name="title"></slot>
</header>
<div class="body">
<slot></slot>
</div>
<footer>
<slot name="footer"></slot>
</footer>
</article>
`;
}
<lit-card>
<h2 slot="title">Billing</h2>
<p>Your plan renews tomorrow.</p>
<button slot="footer" type="button">Manage</button>
</lit-card>
Lit templates support default and named slots just like native Web
Components.
Static Templates for Performance
const loadingTemplate = html`
<p class="loading">Loading...</p>
`;
const emptyTemplate = html`
<p class="empty">No results found.</p>
`;
render() {
if (this.loading) return loadingTemplate;
if (this.items.length === 0) return emptyTemplate;
return html`
<ul>
${this.items.map(
(item) => html`<li>${item}</li>`
)}
</ul>
`;
}
Reuse static templates outside render() when markup
does not depend on changing values. This can improve performance.
Nested Templates
renderItem(item) {
return html`
<li>
<strong>${item.name}</strong>
<span>${item.role}</span>
</li>
`;
}
render() {
return html`
<ul>
${this.users.map((user) => this.renderItem(user))}
</ul>
`;
}
Split complex UI into helper methods that return smaller template
fragments. This keeps render() easier to read.
Lit Templates vs innerHTML
| Feature | Lit Templates | innerHTML |
| Updates | Efficient partial updates | Reparses full HTML string |
| Bindings | Built-in attribute and event bindings | Manual string building |
| Security | Escapes content by default | Riskier with untrusted input |
| Readability | Declarative component UI | String-heavy and harder to maintain |
When to Use Lit Templates
- You are building components with the Lit library.
- You want declarative UI with efficient updates.
- You need bindings for text, attributes, and events.
- You render lists, conditionals, and reusable fragments.
- You combine Shadow DOM styles with component markup.
- You want a cleaner alternative to manual
innerHTML.
Common Lit Template Use Cases
- Cards, alerts, badges, and buttons in a design system.
- Forms with conditional fields and validation messages.
- Lists, tables, and menus rendered from arrays.
- Dialogs and drawers with slot-based layouts.
- Loading, empty, and error states in data components.
- Reusable template fragments for repeated UI patterns.
Lit Template Best Practices
- Keep
render() focused on markup and composition. - Use helper methods for repeated template fragments.
- Prefer static templates for unchanging markup blocks.
- Use property bindings for rich JavaScript values.
- Use boolean and event binding syntax consistently.
- Avoid unnecessary re-renders by keeping state updates clean.
- Combine templates with slots for flexible component APIs.
Common Lit Template Mistakes
- Putting side effects inside
render(). - Building large HTML strings instead of using
html`...`. - Forgetting to return a template from
render(). - Using
innerHTML inside Lit components unnecessarily. - Creating new functions inline on every render without need.
- Not using nested
html templates for conditionals and lists. - Mixing business logic and rendering in one large method.
Key Takeaways
- Lit templates use the
html tagged template literal. render() returns the component's declarative UI. - Bindings handle text, attributes, properties, and events.
- Conditionals, lists, slots, and static templates are common patterns.
- Lit templates are efficient and readable for modern Web Components.
Pro Tip
Treat render() as a pure view function: return
templates based on state, move event handling and data work to
separate methods, and extract repeated markup into helpers.