Lit: Components, Templates & Reactive Properties
Lit is a lightweight library (Google) for building standards-based Web Components -- real Custom Elements that work in any framework or none at all, since the browser itself understands them natively. Templates use plain JavaScript tagged template literals, no JSX compile step required.
A Basic Component
import { LitElement, html, css } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
@customElement('my-counter')
export class MyCounter extends LitElement {
// Scoped to this component's Shadow DOM -- won't leak out or
// be affected by page-level CSS
static styles = css`
button { font-size: 1rem; padding: 4px 12px; }
.count { font-weight: bold; }
`;
// Public reactive property -- reflects to/from an HTML attribute
@property({ type: Number }) startAt = 0;
// Internal reactive state -- not exposed as an attribute
@state() private _count = 0;
connectedCallback() {
super.connectedCallback();
this._count = this.startAt;
}
render() {
return html`
<p>Count: <span class="count">${this._count}</span></p>
<button @click=${this._increment}>+1</button>
`;
}
private _increment() {
this._count++; // setting a reactive property schedules an efficient re-render
}
}
// Usage anywhere -- React JSX, Vue templates, or plain HTML:
// <my-counter start-at="5"></my-counter>Binding Syntax
html`
<!-- Plain attribute -- serializes to a string -->
<div id=${this.elementId}></div>
<!-- ?attr -- boolean attribute, present/absent based on truthiness -->
<button ?disabled=${this.isLoading}>Submit</button>
<!-- .prop -- sets the DOM PROPERTY directly, bypassing attribute
serialization -- important for form inputs where property and
attribute can diverge -->
<input .value=${this.text} />
<!-- @event -- declarative event listener, Lit manages
addEventListener/removeEventListener as the template updates -->
<button @click=${this._onSave}>Save</button>
`;Directives
import { repeat } from 'lit/directives/repeat.js';
import { classMap } from 'lit/directives/class-map.js';
render() {
return html`
<!-- Efficient KEYED list rendering -- like React's key prop -->
<ul>
${repeat(this.items, (item) => item.id, (item) => html`
<li class=${classMap({ active: item.id === this.selectedId })}>
${item.name}
</li>
`)}
</ul>
`;
}Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free