Skip to content

Expressions

Attribute values are JavaScript. Before compiling, sigmx rewrites every $name into $.name, a property of the root store proxy, and every @name( into a call on the functions object; strings, template text, comments and regular expressions are left exactly as written. The result is compiled as a strict-mode function, so all of the following are ordinary JavaScript:

$count++
$user.name = 'Ada'
$items.push({ done: false })
$['odd-key']
$ // the whole store as an object
$total = $items.reduce((sum, i) => sum + i.price, 0)
Live

<div data-signals="{ items: ['tea'], draft: '' }">
<div class="row">
<input data-bind:draft placeholder="item">
<button data-on:click="if ($draft) { $items.push($draft); $draft = '' }">Add</button>
<button data-on:click="$items = []">Clear</button>
</div>
<p data-text="$items.length + ' item(s): ' + $items.join(', ')"></p>
</div>

Directives that need a value (text, show, class, computed, …) evaluate the expression and use the value of the last statement. Directives that run code (on, init, effect, …) run the body as statements.

<span data-text="$a = 1; $b = 2; $a + $b"></span> <!-- 3 -->

Arrow functions, template literals, optional chaining and anything else the browser supports are fine.

Expressions run in strict mode. A misspelt name such as cuont = 1 throws ReferenceError instead of silently creating a global, this is undefined, and assignments to read-only properties throw. A misspelt signal, $cuont, reads as undefined like any missing store path.

Four directives take a literal instead of an expression, because their values are almost always literal text: mask (a pattern), match-media (a media query), teleport (a selector) and remove-me (a delay). Add __dynamic to any of them to write an expression instead.

name meaning
$name the signal at path name
$ the root store proxy; $.user.name, Object.keys($), JSON.stringify($)
el the element the attribute is on
evt the event, in data-on and other event-driven directives
patch the changed signals, in data-on-signal-patch
@name(...) a registered function, e.g. @get('/x')

Globals such as window, document, Math and fetch resolve normally.

An expression that runs inside an effect subscribes to every signal it reads. Reading through $, through $.path, or by enumerating keys all count. Writing a signal inside the same effect that reads it is a loop; sigmx stops it after a bounded number of runs and reports the error.

To read without subscribing, use @peek(() => $x).

Compiled expressions are cached by source text, so a table with a thousand data-text="$row.total" cells compiles that expression once.

By default expressions compile with new Function. On pages that forbid it, pass the nonce-based compiler; see CSP.