--- title: "@state" description: "Use @state for internal reactive host state." group: Decorators order: 4 --- # @state `@state` is the preferred name for internal reactive state in Radiant. Use it for values that should trigger updates but are not part of the custom element's public attribute API. ## Example ```typescript import { RadiantElement, customElement, onEvent, state } from '@ecopages/radiant'; @customElement('counter-display') export class CounterDisplay extends RadiantElement { @state count = 0; @onEvent({ selector: 'button', type: 'click' }) increment() { this.count += 1; } override render() { return (

Count: {this.$.count}

); } } ``` ## How It Works Behind The Scenes `@state` registers host-managed internal mutable state. Under the hood it sets up a tracked state member on the host. Writes update that member state, which notifies `@onUpdated` listeners and keeps JSX bindings in sync. Render invalidation happens through the reactive render path when `render()` reads reactive members — no `@onUpdated` decorator is required for that. The difference is naming and intent: - `@prop(...)` describes public external input. - `@state` describes internal mutable state. That split matters in docs, code review, and JSX typing because it keeps the public element contract separate from implementation-only state. Unlike [@signal](/docs/decorators/signal), the field stays a plain host property. You read and write it with normal property syntax such as `this.count += 1`. ## JSX Binding Defaults Just like `@prop(...)`, `@state` exposes companion bindings by default when `bind` is omitted. So `@state count = 0` gives you `this.$.count` automatically on `RadiantElement` hosts. ```tsx override render() { return

Count: {this.$.count}

; } ``` ## When To Use It - Use `@state` for local UI state. - Use `@prop(...)` for public element API. - Use `@signal` when the field itself should be a writable signal instance. See [@prop](/docs/decorators/prop) and [RadiantElement](/docs/components/radiant-element).