---
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).