Skip to main content
This guide walks you through creating a working Lit component from scratch. By the end, you’ll have a reactive custom element running in the browser.
1

Install Lit

Add Lit to your project using your package manager of choice.
npm install lit
Lit 3.x targets modern browsers and requires ES2019+ support. If you need older browser support, see the installation guide.
2

Create your component

Create a file named my-greeting.ts and define your first Lit component.
my-greeting.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('my-greeting')
export class MyGreeting extends LitElement {
  static styles = css`
    :host {
      display: block;
      font-family: sans-serif;
    }

    p {
      color: #344cfc;
      font-size: 1.25rem;
    }
  `;

  @property()
  name = 'World';

  render() {
    return html`<p>Hello, ${this.name}!</p>`;
  }
}
A few things to note:
  • @customElement('my-greeting') registers my-greeting as a custom element and is equivalent to calling customElements.define('my-greeting', MyGreeting).
  • @property() declares name as a reactive property. Any change to name automatically schedules a re-render.
  • static styles uses the css tagged template to define Shadow DOM-scoped styles.
  • render() returns a html tagged template that Lit efficiently updates on each change.
3

Use the component in HTML

Import your component and use it like any HTML element.
index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My Lit App</title>
    <script type="module" src="./my-greeting.js"></script>
  </head>
  <body>
    <my-greeting name="Lit"></my-greeting>
  </body>
</html>
The name attribute maps directly to the name property because Lit observes declared properties and reflects attribute changes automatically.
4

Update properties reactively

Reactive properties trigger a re-render whenever they change — whether set from an attribute, via JavaScript, or from within the component itself.
my-greeting.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('my-greeting')
export class MyGreeting extends LitElement {
  static styles = css`
    :host {
      display: block;
      font-family: sans-serif;
    }

    p {
      color: #344cfc;
      font-size: 1.25rem;
    }

    button {
      margin-top: 0.5rem;
      cursor: pointer;
    }
  `;

  @property()
  name = 'World';

  render() {
    return html`
      <p>Hello, ${this.name}!</p>
      <button @click=${this._handleClick}>Change name</button>
    `;
  }

  private _handleClick() {
    this.name = this.name === 'World' ? 'Lit' : 'World';
  }
}
Clicking the button updates this.name, which schedules an efficient re-render — only the changed parts of the DOM are updated.
Lit batches property changes and performs a single asynchronous update per microtask, so setting multiple properties at once is just as efficient as setting one.

Next steps

You now have a working Lit component with reactive properties and scoped styles. Explore the core concepts to go further:

Components

Understand the LitElement lifecycle, shadow root, and component anatomy.

Templates

Learn how html tagged templates work and how to bind expressions.

Reactive properties

Explore the full @property and @state options for fine-grained reactivity.

Styles

Use css tagged templates, CSS custom properties, and dynamic styles.