@property() decorator and the static properties object.
Declaring properties with @property()
requestUpdate() and the component re-renders asynchronously.
Declaring properties without decorators
Use the staticproperties getter for plain JavaScript or decorator-free TypeScript:
Property options (PropertyDeclaration)
Both @property() and the properties object accept a PropertyDeclaration options object:
attribute
Controls the HTML attribute name that maps to this property.
fooBar → foobar).
type
A type hint passed to the converter. The default converter handles String, Number, Boolean, Array, and Object:
reflect
When true, setting the property also updates the corresponding attribute via the converter’s toAttribute method:
converter
Customizes how attribute strings are converted to property values and back.
fromAttribute function:
hasChanged
Determines whether a new value should trigger an update. By default, Lit uses Object.is() (strict identity check):
hasChanged for deep equality checks on objects or arrays:
Default attribute converters
The built-indefaultConverter maps type hints to DOM attributes:
| Type | fromAttribute behavior | toAttribute behavior |
|---|---|---|
String | Returns the string as-is | Returns the string as-is |
Number | Number(value), or null if attribute absent | Returns the number as-is |
Boolean | true if attribute present, false if absent | "" if true, null (removes) if false |
Object | JSON.parse(value) | JSON.stringify(value) |
Array | JSON.parse(value) | JSON.stringify(value) |
@state() for internal reactive state
Use @state() for private data that drives rendering but shouldn’t be part of the public API:
@state() is shorthand for @property({ state: true, attribute: false }). State properties:
- Trigger reactive updates when changed.
- Are not observed as attributes.
- Are not reflected to attributes.
- May be renamed by minifiers.
As a convention, prefix
@state() properties with an underscore (_count) to signal they are internal.The property update cycle
When you set a reactive property, Lit schedules an asynchronous update:Property setter is called
The generated accessor reads the old value, sets the new value, and calls
requestUpdate(name, oldValue, options).requestUpdate() checks hasChanged
If
hasChanged(newValue, oldValue) returns true, the property is recorded in _$changedProperties and an async update is enqueued if one isn’t already pending.Update is batched
Multiple property changes in the same microtask are batched into a single update cycle. The update runs after the current call stack clears.
PropertyValues and changedProperties
Lifecycle methods like willUpdate(), update(), firstUpdated(), and updated() receive a changedProperties map:
PropertyValues<this> gives strongly-typed access to key/value pairs on this.