---
title: "State and expressions"
description: "State variables, two-way binding, operators, built-in functions, @each, conditionals, queries and action lists in GistUI Lang."
---

> Documentation Index
> Fetch the complete documentation index at: https://gistui.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# State and expressions

A program can be more than a static layout. It can hold state, compute values and react to input, all in the browser, with no round trip to the model.

## State

`$name = value` declares a state variable. `bind:$name` ties a control to it, both ways.

```gistui
$plan = "Team"
$seats = 12
root = Card(RadioGroup("plan", "Plan", ["Starter", "Team", "Enterprise"], v:segmented, bind:$plan), Slider("seats", "Seats", min:1, max:100, bind:$seats, unit:" seats"), Stat("Monthly cost", @fmt($seats * ($plan == "Starter" ? 0 : $plan == "Team" ? 29 : 79), "$"), note:$plan + " plan"))
```

`bind:` works on Input, TextArea, Select, Combobox, RadioGroup, CheckboxGroup, TagInput, Checkbox, Switch, Slider, DatePicker, TimePicker, Tabs (the selected label) and Chart (the selected x).

A host can set starting values with `initialState`, and listen with `onStateChange`.

## Operators

| Kind | Operators |
|---|---|
| Arithmetic | `+` `-` `*` `/` `%` |
| Comparison | `==` `!=` `<` `>` `<=` `>=` |
| Logic | `&&` `||` `!` |
| Conditional | `a ? b : c` |
| Access | `a.b`, `a[0]` |

`+` joins text when either side is text. A conditional can choose between components:

```gistui
body = $tab == "done" ? doneView : formView
```

## Lists and `@each`

A pipe table, or a query result, is a list of rows. `@each` makes one component per row.

```gistui
root = Stack(Header("Large orders", "Over $1,000", size:sm), Grid(big, cols:3))
big = @each(@filter(orders, o => o.Total > 1000), o => Tile(o.Customer, o.Status, value:@fmt(o.Total, "$"), tone:o.Status == "Refunded" ? "danger" : "success"))
orders = |Customer|Total:n|Status
|Lumen Labs|1200|Paid
|Kite & Co|240|Paid
|Brightwave|9800|Paid
|Orbital|4300|Refunded
```

`rows.total` plucks one field from every row, so `@sum(orders.Total)` adds a column.

## Built-in functions

| Function | Returns |
|---|---|
| `@count(list)` | number of items |
| `@first(list)`, `@last(list)` | one item |
| `@sum(list)`, `@avg(list)`, `@min(list)`, `@max(list)` | a number |
| `@sort(list, key)` | a sorted list |
| `@filter(list, x => test)` | the items that pass |
| `@each(list, x => Component)` | one component per item |
| `@join(list, sep)` | text |
| `@round(n, digits)`, `@abs(n)`, `@floor(n)`, `@ceil(n)` | a number |
| `@fmt(value, kind)` | formatted text; `kind` is `"$"`, `"%"`, `"compact"` or `"date"` |

There is nothing else. A program cannot define functions, loop without bound or call into the page.

## Queries and mutations

```gistui
sales = @query("get_sales", {range:$range}, default:[], every:60)
save = @mutation("update_goal", {goal:$goal})
```

A query reads through your `tools` and re-runs when its arguments change. A mutation runs only from a button. Both are covered in [Tools and live data](/docs/guides/tools-and-data/).

## Action lists

`do:[…]` on a Button, Tile, Media, Hero or Pricing runs steps in order and stops at the first failure.

```gistui
btn = Button("Save", do:[@run(save), @set($tab, "done"), @send("Goal saved")])
```

| Step | Effect |
|---|---|
| `@set($x, value)` | set state |
| `@reset($x, …)` | back to the declared defaults |
| `@run(name)` | run a mutation, or re-fetch a query |
| `@send("text")` | a `send` action to your app |
| `@open(url)` | open a link |
| `@emit("event", payload)` | a custom event to your app |

What reaches your `onAction` is described in [Actions and forms](/docs/guides/actions-and-forms/).

## How it is evaluated

- Expressions are interpreted, never compiled: no `eval`, no `new Function`.
- Each expression tracks what it read. A state change re-evaluates only the props that depend on it.
- Evaluation is bounded: 500,000 steps per pass, 5,000 items per `@each`. See [limits](/docs/protocol/format/#limits).

Source: https://gistui.com/docs/language/state-and-expressions/index.mdx
