TypeScript decorators for defining reactive properties, internal state, and element metadata with automatic change detection and re-rendering.
Class decorator for defining custom elements with automatic registration.
/**
* Class decorator that defines a custom element
* Automatically calls customElements.define() with the provided tag name
*/
function customElement(tagName: string): ClassDecorator;
type CustomElementDecorator = typeof customElement;Usage Examples:
import { LitElement } from "lit";
import { customElement } from "lit/decorators.js";
@customElement("my-button")
class MyButton extends LitElement {
// Component implementation
}
// Equivalent to:
// customElements.define("my-button", MyButton);Property decorator for observed reactive properties that trigger re-renders when changed.
/**
* Property decorator for reactive properties
* Properties trigger re-renders when changed and can be bound to attributes
*/
function property(options?: PropertyDeclaration): PropertyDecorator;
type PropertyDecorator = (target: any, propertyKey: PropertyKey) => void;
/**
* Standard property decorator implementation
* Used internally by the property decorator
*/
function standardProperty(
options?: PropertyDeclaration,
element?: ReactiveElement
): PropertyDecorator;Usage Examples:
import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators.js";
@customElement("user-card")
class UserCard extends LitElement {
// Basic reactive property
@property() name = "";
// Property with type hint
@property({ type: Number }) age = 0;
// Property with custom attribute name
@property({ attribute: "full-name" }) fullName = "";
// Property that reflects to attribute
@property({ reflect: true }) status = "active";
// Property with custom converter
@property({
converter: {
fromAttribute: (value) => JSON.parse(value || "{}"),
toAttribute: (value) => JSON.stringify(value)
}
}) data = {};
render() {
return html`
<div>
<h2>${this.name}</h2>
<p>Age: ${this.age}</p>
<p>Status: ${this.status}</p>
</div>
`;
}
}Property decorator for internal component state that triggers re-renders but is not reflected to attributes.
/**
* Property decorator for internal state
* State properties trigger re-renders but are not reflected to attributes
*/
function state(): PropertyDecorator;
/** Configuration interface for state declarations */
interface StateDeclaration {
hasChanged?(value: unknown, old: unknown): boolean;
}
/** Internal property declaration type */
type InternalPropertyDeclaration = PropertyDeclaration;Usage Examples:
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
@customElement("toggle-button")
class ToggleButton extends LitElement {
// Public property
@property() label = "Toggle";
// Internal state - not reflected to attributes
@state() private _pressed = false;
private _handleClick() {
this._pressed = !this._pressed;
}
render() {
return html`
<button @click=${this._handleClick} ?pressed=${this._pressed}>
${this.label}: ${this._pressed ? "ON" : "OFF"}
</button>
`;
}
}Method decorator for configuring event listener options on event handler methods.
/**
* Method decorator for event listener options
* Configures passive, once, and capture options for event handlers
*/
function eventOptions(options: AddEventListenerOptions): MethodDecorator;
type EventOptionsDecorator = typeof eventOptions;Usage Examples:
import { LitElement, html } from "lit";
import { customElement, eventOptions } from "lit/decorators.js";
@customElement("scroll-handler")
class ScrollHandler extends LitElement {
// Passive event listener for better scroll performance
@eventOptions({ passive: true })
private _handleScroll(e: Event) {
console.log("Scroll event:", e);
}
// One-time event listener
@eventOptions({ once: true })
private _handleFirstClick(e: Event) {
console.log("First click only:", e);
}
// Capturing event listener
@eventOptions({ capture: true })
private _handleCapture(e: Event) {
console.log("Captured event:", e);
}
render() {
return html`
<div
@scroll=${this._handleScroll}
@click=${this._handleFirstClick}
@mousedown=${this._handleCapture}
>
Scrollable content
</div>
`;
}
}/** Class decorator type for custom elements */
type ClassDecorator = <T extends Constructor>(target: T) => T | void;
/** Property decorator type for reactive properties */
type PropertyDecorator = (target: any, propertyKey: PropertyKey) => void;
/** Method decorator type for event options */
type MethodDecorator = <T>(
target: any,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
) => TypedPropertyDescriptor<T> | void;
/** Constructor type for class decorators */
type Constructor<T = {}> = new (...args: any[]) => T;/** Configuration options for reactive properties */
interface PropertyDeclaration<Type = unknown> {
/**
* Attribute name for property binding
* Set to false to disable attribute binding
* Set to string to use custom attribute name
*/
attribute?: boolean | string;
/** Type hint for automatic conversion */
type?: TypeHint;
/** Custom attribute converter */
converter?: AttributeConverter<Type>;
/** Whether to reflect property changes to attributes */
reflect?: boolean;
/** Custom function to determine if property has changed */
hasChanged?(value: Type, oldValue: Type): boolean;
}
/** Type hints for property conversion */
type TypeHint =
| typeof String
| typeof Number
| typeof Boolean
| typeof Array
| typeof Object;
/** Custom attribute converter interface */
interface AttributeConverter<Type = unknown, TypeHint = unknown> {
/** Convert attribute string to property value */
fromAttribute?(value: string | null, type?: TypeHint): Type;
/** Convert property value to attribute string */
toAttribute?(value: Type, type?: TypeHint): unknown;
}/** Options for event listener configuration */
interface AddEventListenerOptions {
/** Event listener will not call preventDefault() */
passive?: boolean;
/** Event listener will be removed after first invocation */
once?: boolean;
/** Event listener will be called during capture phase */
capture?: boolean;
/** AbortController signal for cancellation */
signal?: AbortSignal;
}Properties decorated with @property() or @state() automatically trigger component updates when their values change. The default change detection uses Object.is() comparison, but can be customized with the hasChanged option.
Properties with @property() are automatically bound to HTML attributes:
userName → user-name)The type option provides automatic conversion between attributes and properties:
String: No conversion (default)Number: Number(value)Boolean: value !== nullArray, Object: JSON.parse(value)@state() for internal properties to avoid attribute reflection overheadhasChanged functions for complex objects to prevent unnecessary updates@eventOptions({ passive: true }) for scroll and touch events