or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-components.mdcustom-directives.mddom-query-decorators.mdindex.mdproperty-decorators.mdstatic-templates.mdtemplate-directives.md
tile.json

property-decorators.mddocs/

Property Decorators

TypeScript decorators for defining reactive properties, internal state, and element metadata with automatic change detection and re-rendering.

Capabilities

Custom Element Decorator

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

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>
    `;
  }
}

State Decorator

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>
    `;
  }
}

Event Options Decorator

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>
    `;
  }
}

Types

Decorator Types

/** 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;

Property Declaration Options

/** 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;
}

Event Listener Options

/** 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;
}

Property System Behavior

Change Detection

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.

Attribute Binding

Properties with @property() are automatically bound to HTML attributes:

  • Property names are converted to lowercase attribute names
  • Camel case properties become dash-case attributes (userNameuser-name)
  • Boolean properties create boolean attributes (presence/absence)
  • Non-primitive values are JSON serialized by default

Type Conversion

The type option provides automatic conversion between attributes and properties:

  • String: No conversion (default)
  • Number: Number(value)
  • Boolean: value !== null
  • Array, Object: JSON.parse(value)

Performance Considerations

  • Use @state() for internal properties to avoid attribute reflection overhead
  • Implement custom hasChanged functions for complex objects to prevent unnecessary updates
  • Consider using @eventOptions({ passive: true }) for scroll and touch events