Before building real components, you need a mental model of how the pieces of a Lit component relate to each other. This lesson walks through the building blocks every Lit component shares.
The Building Blocks of a Lit Component
Every Lit component, no matter how complex, is assembled from the same handful of ingredients: a class extending LitElement, a tag name registered with the browser, reactive properties describing its data, a render() method returning a template, and optional scoped styles.
Once you can identify these pieces in any Lit component, you can read (and confidently modify) components you did not write yourself, whether they use the TypeScript decorator syntax or the plain JavaScript static properties API.
Each component typically lives in its own file, named after its tag, e.g. counter-button.ts.
An entry file (often index.ts) imports every component so its side-effecting @customElement registration runs.
package.json lists lit as a dependency; TypeScript projects also add typescript and enable decorators.
No build step is strictly required — Lit works with native ES modules directly in a modern browser during development.
Lit Basics Cheat Sheet
The essential vocabulary you need before writing your first real component.
Term
Meaning
LitElement
The base class every Lit component extends
html
Tagged template function that produces a renderable template
css
Tagged template function that produces scoped styles
render()
Method returning the component's current template
Reactive property
A field that triggers a re-render when it changes
Custom element tag
The lowercase, hyphenated HTML tag name, e.g. my-app
Shadow DOM
The encapsulated DOM tree Lit renders your template into by default
The static properties Field
In plain JavaScript (without decorators), you declare reactive properties using a static properties getter or field on the class. Lit reads this object at class-definition time to know which fields to watch for changes and how to convert them to/from HTML attributes.
TypeScript projects usually prefer the equivalent @property() decorator, covered in a later lesson.
Starting a New Lit Project
The fastest way to try Lit is the official starter templates, which scaffold a minimal TypeScript or JavaScript project with a build setup (Vite) already configured.
npm init @open-wc — scaffolds a Lit/Web Component project with sensible defaults.
npm install lit — adds Lit to an existing project (works with any bundler or none).
Use a <script type="module"> tag to load your compiled component file directly in index.html.
Use each component's tag name directly in HTML once its module has been imported, e.g. <counter-button></counter-button>.
Common Mistakes
Forgetting to call customElements.define() (or apply @customElement), so the browser never recognizes the tag.
Declaring a property without listing it in static properties (or @property), so Lit never re-renders when it changes.
Mixing up html template output with a plain string — templates must be returned from render(), not concatenated as text.
Not realizing styles in static styles are scoped to the component's Shadow DOM and won't leak in or out by default.
Key Takeaways
Every Lit component is built from the same pieces: a class, reactive properties, a render template, styles, and registration.
The plain JavaScript static properties API and the TypeScript @property decorator describe the same underlying reactive system.
Lit needs no build step to run in a modern browser, though most real projects use Vite or a similar bundler.
Tag names must be lowercase and contain a hyphen, per the Custom Elements specification.
Pro Tip
When starting out, write one component with the plain static properties API and the same component again with TypeScript decorators. Seeing both side by side makes it obvious that decorators are just sugar over the same underlying property system.
You now understand the core building blocks of a Lit component. Next, set up a real project so you can start writing components.