---
title: "@attr"
description: "Bind a reactive field to a host HTML attribute on a RadiantElement or RadiantController."
group: Decorators
order: 2
---
# @attr
`@attr(...)` binds a reactive field to a host HTML attribute.
Use it when a value should come from authored host markup rather than from JSX property transport.
That makes it the right fit for:
- `data-*` configuration on `RadiantController`
- ordinary host attributes such as `title`, `lang`, `hidden`, or `aria-*`
- HTML-authored custom elements that should read plain host attributes reactively
- interoperable values that should stay visible on the host markup
Use [@prop](/docs/decorators/prop) when the field is part of a custom element's property API and should participate in JSX `prop:` transport.
Important: `@attr(...)` binds the decorated field to the host attribute channel. It is not the primary tool for setting attributes on child elements inside `render()`.
## Example
```ts
import { RadiantElement, attr, customElement } from '@ecopages/radiant';
@customElement('status-pill')
export class StatusPill extends RadiantElement {
@attr({ source: 'data-status' }) declare status: string | undefined;
@attr({ source: 'aria-label' }) declare label: string | undefined;
override render() {
return {this.status ?? 'unknown'};
}
}
```
```html
```
In this example:
- `status` reads from the host `data-status` attribute
- `label` reads from the host `aria-label` attribute
- assigning either field writes back through that same host attribute channel
When `type` is provided, `@attr(...)` uses the same built-in attribute codec family as `@prop(...)` for `String`, `Number`, `Boolean`, `Object`, and `Array`.
## Options
| Option | Type | Meaning |
| --- | --- | --- |
| `source` | `string` | Attribute name to read and write. Defaults to the kebab-cased field name. |
| `type` | `String \| Number \| Boolean \| Object \| Array` | Built-in attribute codec to use when no custom converter is provided. |
| `defaultValue` | `T` | Value used before the attribute is present. |
| `bind` | `boolean \| string` | Companion JSX binding accessor to expose. |
| `converter` | `{ fromAttribute, toAttribute }` | Custom conversion hooks for parsing and serializing attribute values. Overrides `type` when provided. |
## Default Conversion
Use `type` for the common cases where the host attribute should behave like a typed `@prop(...)` value without becoming part of the custom element's public property API.
```ts
import { RadiantController, attr } from '@ecopages/radiant';
class SearchController extends RadiantController {
@attr({ source: 'data-page', type: Number }) page = 1;
@attr({ source: 'data-enabled', type: Boolean }) enabled = false;
@attr({ source: 'title' }) title?: string;
}
```
Use `converter` only when the built-in codecs are not enough.
## Element vs Controller Usage
- On `RadiantElement`, `@attr(...)` is useful for host markup-visible configuration.
- On `RadiantController`, it is the preferred way to read host-authored controller inputs, often through `data-*` but not limited to them.
## Important
`@attr(...)` is host-attribute-driven. It does not replace `@prop(...)` for JSX property APIs.
If a custom element needs both channels, keep them explicit:
- `@prop(...)` for JSX and property-driven composition
- `@attr(...)` for host-authored attributes
If you need to set attributes on child nodes produced by `render()`, use normal JSX attributes or the `data={{ ... }}` / `aria={{ ... }}` object forms instead of `@attr(...)`.
## See Also
- [@prop](/docs/decorators/prop)
- [RadiantController](/docs/components/radiant-controller)