Bindings Reference
On this page 23
Bindings reactively synchronize data between your component state and the DOM. They use the x- prefix or the shorthand : prefix.
All binding expressions are evaluated in the component's reactive scope. Signal values are auto-unwrapped — if count is a signal, writing x-text="count" evaluates to the signal's value, not the signal function itself.
x-text
Sets the element's textContent reactively. HTML is escaped.
<spanx-text=""></span>
<spantext=""></span>
| Attribute | Type | Description |
|---|---|---|
x-text | string (expression) | Expression whose result replaces the element's text content |
Aliases: :text, @text
x-html
Sets the element's innerHTML reactively. The expression result is inserted as raw HTML.
<divx-html=""></div>
<divhtml="<strong></strong>"></div>
| Attribute | Type | Description |
|---|---|---|
x-html | string (expression) | Expression whose result replaces the element's inner HTML |
Aliases: :html, @html
Warning:
x-htmlinserts unescaped HTML. Never bind user-supplied content without sanitization. Use the@stacksjs/sanitizerpackage or server-side sanitization to prevent XSS.
x-class
Adds or removes CSS classes reactively. Supports three syntax forms.
Ternary / expression syntax
<divx-class=""></div>
Object syntax
Keys are class names, values are boolean expressions:
<divx-class=""></div>
Array syntax
<divx-class=""></div>
| Attribute | Type | Description |
|---|---|---|
x-class | string | object | array (expression) | Classes to add/remove reactively |
Aliases: :class
Note:
x-classmerges with any staticclassattribute on the element. It does not replace existing classes.
x-style
Sets inline styles reactively.
String syntax
<divx-style=""></div>
Object syntax
<divx-style=""></div>
| Attribute | Type | Description |
|---|---|---|
x-style | string | object (expression) | Inline styles to apply reactively |
Aliases: :style
x-model
Two-way binding between a form element and a signal. Listens to input events on text inputs/textareas and change events on checkboxes/selects/radios.
<inputtype=""x-model="" />
<textareax-model=""></textarea>
<selectx-model="">
<optionvalue="">A</option>
<optionvalue="">B</option>
</select>
<inputtype=""x-model="" />
| Attribute | Type | Description |
|---|---|---|
x-model | string (signal name) | Signal to bind bidirectionally to the form element's value |
Aliases: :model, @model
The bound signal is updated via signal.set() whenever the user types or changes the input. The element's value/checked state is updated whenever the signal changes.
Supported element types
| Element | Reads | Writes |
|---|---|---|
<input type="text"> | .value | input event |
<input type="checkbox"> | .checked | change event |
<input type="radio"> | .value (when checked) | change event |
<textarea> | .value | input event |
<select> | .value | change event |
A path is allowed, and resolves to the signal at the end of it — x-model="store.title"
and x-model="settings.theme" both call .set() on the signal held there, so every
other binding and effect reading it updates too.
Warning: the value at the end of the path has to BE a signal for anything to re-render.
x-model="draft.title"over a plain object stores what you type, but nothing is watching a plain property, so the rest of the page will not update. This used to be worse than a no-op — the write replaced the signal with the raw value, silently breaking every other consumer of it (stacksjs/stx#1883).
x-show
Toggles element visibility by setting display: none when the expression is falsy.
<divx-show="">Dropdown content</div>
<px-show="">Showing results</p>
| Attribute | Type | Description |
|---|---|---|
x-show | string (expression) | When falsy, element gets display: none; when truthy, display is restored |
Aliases: :show, @show
Note: The element remains in the DOM. For conditional rendering that adds/removes elements, use
:if/x-if.
x-if
Conditionally inserts or removes the element from the DOM.
<divx-if="">Welcome back</div>
<px-if="">Something went wrong: <spanx-text=""></span></p>
| Attribute | Type | Description |
|---|---|---|
x-if | string (expression) | When truthy, element is in the DOM; when falsy, replaced with a comment placeholder |
Aliases: :if, @if
When the condition becomes truthy again, the element is re-inserted and its children are re-processed (bindings re-applied). Child processing is deferred via setTimeout(0) to prevent nested effects from subscribing to the parent's tracked signals.
Warning: Avoid the
>operator inx-ifexpressions inside HTML attributes — the regex-based parser may interpret it as a tag closer. Use>=or negate the condition instead:<!-- Bad: may break --> <div x-if="count > 0">...</div> <!-- Good --> <div x-if="count >= 1">...</div>
x-for
Renders the element once for each item in an array.
<divx-for="">
<spanx-text=""></span>
</div>
With index
Parenthesized syntax (Alpine-compatible):
<lix-for="">
<spanx-text=""></span>. <spanx-text=""></span>
</li>
With key
<divx-for=""key="">
<px-text=""></p>
</div>
| Attribute | Type | Description |
|---|---|---|
x-for | string | Iteration expression: item in array or (item, index) in array |
:key | string (expression) | Unique key per item for efficient DOM diffing |
Aliases: :for, @for
Important: Place
x-fordirectly on the element to be repeated. Do not use<template x-for>— stx strips<template>tags during server processing.
x-ref
Assigns a DOM element reference accessible via useRef().
<inputx-ref=""type="" />
<canvasx-ref=""></canvas>
<script
const input = useRef('searchInput')
onMount(() => {
input.current.focus()
})
</script>
| Attribute | Type | Description |
|---|---|---|
x-ref | string | Name to register the element under in $refs |
Aliases: :ref
x-cloak
Hides the element until the signals runtime processes it. Prevents a flash of unprocessed template content (FOUC).
<divx-cloak>
<spanx-text=""></span>
</div>
The runtime removes the x-cloak attribute after processing. Pair with CSS:
[x-cloak] { display: none !important; }
| Attribute | Type | Description |
|---|---|---|
x-cloak | boolean (no value) | Removed by runtime after element is processed |
x-bind (generic attribute binding)
Any x-bind:attr or shorthand :attr dynamically sets an HTML attribute.
<ax-bind:href="">Profile</a>
<imgsrc=""alt="" />
<buttondisabled="">Submit</button>
<inputplaceholder="" />
| Syntax | Description |
|---|---|
x-bind:href="expr" | Sets href reactively |
:src="expr" | Shorthand for x-bind:src |
:disabled="expr" | Boolean attribute — removed when falsy |
:aria-label="expr" | Works with any attribute name |
Reserved directive names
The following names are handled by dedicated processors and are not generic attribute bindings:
class, style, text, html, show, model, if, for, ref
Any :attr not in this list becomes a dynamic attribute binding.
x-data
Defines a reactive scope on an element. State properties are wrapped in signals automatically.
<divx-data="">
<px-text=""></p>
<buttonclick=""x-text=""></button>
</div>
With init method
<divx-data="">
<divx-for=""key="">
<px-text=""></p>
</div>
</div>
| Attribute | Type | Description |
|---|---|---|
x-data | string (object literal) | Reactive state scope. Properties become signals. init() runs automatically. |
The init() method supports async — the UI re-renders when the promise resolves. TypeErrors during initial render (before async data loads) are silently caught and re-evaluated when signals update.
Important:
x-datatriggers injection of the signals runtime. The reactive bridge (reactive.ts) parses the object, wraps properties in signals, and registers the scope. The signals runtime then handles all directive processing within that scope.
Double-Bind Guards
All binding functions have internal guards to prevent duplicate processing when processElement is called multiple times on the same element. Each binding sets a flag (e.g., el.__stx_show_bound, el.__stx_model_bound) after first processing. This is an implementation detail — no action required from users.