Skip to main content
The @state decorator declares an internal reactive property. Like @property, changing a state property schedules an element update. Unlike @property, state properties are not backed by HTML attributes and are not intended to be set by users of the element.

Import

import {state} from 'lit/decorators.js';

Signature

state(options?: StateDeclaration): PropertyDecorator

Options

hasChanged
(value: T, oldValue: T) => boolean
A function that determines whether a change to the property should trigger an update. Return true to request an update. Defaults to strict inequality (!==).

Usage

import {LitElement, html} from 'lit';
import {state} from 'lit/decorators.js';

class MyCounter extends LitElement {
  @state()
  private _count = 0;

  render() {
    return html`
      <p>Count: ${this._count}</p>
      <button @click=${() => this._count++}>Increment</button>
    `;
  }
}

@state vs @property

@state@property
Triggers re-renderYesYes
Backed by attributeNoYes (by default)
Visible from HTMLNoYes
Can be renamed by minifiersYesNo
Intended audienceInternal element logicPublic API
Use @state for values that are internal implementation details of your element — things like whether a dropdown is open, or intermediate values computed during user interaction. Use @property for values that element users can configure from HTML or JavaScript, such as a name or disabled flag.
State properties can be renamed or mangled by optimizing compilers like Closure Compiler because they are not tied to observed attribute names. This makes them safe to use with build-time minification.

Without decorators

Without decorators, declare a state property using static properties with state: true:
import {LitElement, html} from 'lit';

class MyCounter extends LitElement {
  static properties = {
    _count: {state: true},
  };

  constructor() {
    super();
    this._count = 0;
  }

  render() {
    return html`
      <p>Count: ${this._count}</p>
      <button @click=${() => this._count++}>Increment</button>
    `;
  }
}