Skip to content
>_devvkit
$devvkit learn --librarie lit-guide

Lit Guide

[web-components][frontend][reactive][custom-elements]
JavaScript / TypeScript
Install
npm create vite@latest my-app -- --template lit-ts
# or: npm init lit

Lit is a web component library from Google. It builds directly on the Custom Elements v1 and Shadow DOM standards. No framework needed — your components work in any HTML page or any framework.

LitElement extends HTMLElement. Reactive properties trigger automatic re-renders. Lit's templating uses tagged template literals (html`...`) — extremely fast with no virtual DOM.

Lit 3 is tree-shakeable and tiny (~5 KB gzipped). It supports decorators (@property, @customElement) or the `static properties` pattern. SSR via @lit-labs/ssr.

Setup

Create appScaffold Lit project.
npm init lit@latest my-app
cd my-app
npm run dev

Component

Basic componentLit element.
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 { color: blue; }`

  @property() name = 'World'

  render() {
    return html`<p>Hello, ${this.name}!</p>`
  }
}

<!-- Usage in HTML: -->
<my-greeting name="Lit"></my-greeting>

Reactive Properties

Reactive propertiesFine-grained reactivity.
class MyElement extends LitElement {
  // Attribute reflects to property
  @property({ type: String }) name = 'Alice'
  // No attribute reflection
  @property({ attribute: false }) data: Record<string, unknown> = {}
  // Boolean attribute
  @property({ type: Boolean }) active = false
}

Templates

Conditional renderTemplates with conditionals.
render() {
  return html`
    ${this.loading
      ? html`<p>Loading...</p>`
      : html`<p>${this.data}</p>`
    }
  `
}
ListsRepeat templates.
render() {
  return html`
    <ul>
      ${this.items.map(item => html`<li>${item.name}</li>`)}
    </ul>
  `
}

Events

EventsHandle user events.
class MyButton extends LitElement {
  @property() label = 'Click'

  private handleClick(e: Event) {
    this.dispatchEvent(new CustomEvent('my-click', { detail: { time: Date.now() } }))
  }

  render() {
    return html`<button @click=${this.handleClick}>${this.label}</button>`
  }
}

Lifecycle

LifecycleReactive update cycle.
class MyElement extends LitElement {
  @property() data: string[] = []

  willUpdate(changed: PropertyValues<this>) {
    if (changed.has('data')) {
      this.processData(this.data)
    }
  }

  updated(changed: PropertyValues<this>) {
    console.log('Component updated')
  }
}