Skip to content

LitElement

Lit ships as a single npm package. In a build-tool project you install it once:

Terminal window
npm install lit

Then import the pieces you need:

import { LitElement, html, css } from 'lit';

In the playground runner on this site we use a CDN dynamic import (the async IIFE pattern you have seen in earlier lessons):

const { LitElement, html, css } = await import('https://esm.sh/lit');

Declare reactive properties with the static class field static properties. Each key is a property name; the value is an options object with at minimum a type hint:

static properties = {
count: { type: Number },
label: { type: String },
active: { type: Boolean },
};

When any of these properties changes, Lit automatically schedules an asynchronous re-render.

The render() method returns a TemplateResult — a tagged template literal processed by html`...`. Lit diffs the result against the previous render and patches only what changed:

render() {
return html`
<p>${this.label}: ${this.count}</p>
<button @click=${() => this.count++}>Increment</button>
`;
}

Add scoped CSS with the static styles class field using the css`...` tagged template. Styles are adopted once into the element’s Shadow DOM stylesheet — they never leak out to the page:

static styles = css`
:host { display: block; padding: 1rem; font-family: sans-serif; }
button { background: #6200ee; color: white; border: none; border-radius: 4px; padding: 6px 12px; cursor: pointer; }
p { margin: 0 0 8px; }
`;

When you write this.count++, the setter generated by Lit calls requestUpdate() internally. The update is batched at microtask timing, so multiple property changes in one tick produce a single render pass.

What does declaring a property in static properties do?
What does the html tagged template literal return?
How does Lit scope CSS to only the component's shadow tree?