Expressions

How values are resolved anywhere one is expected, from literals and references to scoped arguments.

Resolution and Scoping

Two rules govern how values are read:

Expressions work anywhere a value is expected: a prop value, a conditional's subject or branch, a map's source or template, an action's argument. They also stand in for literals at any depth inside a prop's objects and arrays; these are called deep expressions:

{
  "type": "button",
  "props": {
    "label": "Submit",
    "layout": { "direction": "horizontal", "gap": { "$data": "/gap" } }
  }
}

Resolution is a single pass over the document: a value an expression returns is data, never re-interpreted as an expression. A data value shaped like { "$data": ... } resolves to that object itself, not to a further reference.

Resolution is also a function of the current data context: when the data context changes, resolved values MUST reflect the change (action arguments, resolved at dispatch time, are the stated exception). When and how the engine re-renders is the engine's concern.

Scoped arguments are read through $arg, within the construct that injects them. A map injects $item and $index into its template, an action injects the callback's positional arguments $0, $1, …, and a translation tag injects $content. Outside its construct, a scoped argument is invalid. See Scoped Arguments.

Expression Types

An expression resolves to a single value: a literal, a data reference, a translation reference, an inline message, or a scoped argument. A field binding also appears in value position but additionally registers form state.

Literal Values

Static values matching the prop's expected type:

{
  "type": "button",
  "props": { "label": "Click me", "disabled": false }
}

Data References

Data references resolve to a value from the data context using a JSON Pointer:

{ "$data": "/path/to/field" }

Required

  • $data (string, JSON Pointer RFC 6901): Path to a field in the data. The empty pointer "" resolves to the whole data context.

Returns: The value at that pointer location, or undefined when the path names no value. A prop resolving to undefined is treated as omitted; engines SHOULD report unresolved references during development.

When used in a prop, the prop MUST accept the type that this expression resolves to.

Example

{
  "type": "text",
  "props": { "children": { "$data": "/name" } }
}

Translation References

Translation references resolve to a message from the active locale's catalog:

{ "$t": "form.greeting" }

Required

  • $t (string): Catalog key of the ICU MessageFormat message to format (the classic MessageFormat syntax, not MessageFormat 2). The key is always a static string, so every key a document uses can be extracted without resolving it.

Optional

  • $params (object): Values interpolated into the message. Each value is itself resolved as an expression first, so data references, scoped arguments, and nested translation references are allowed.
  • $tags (object): Elements rendering the message's tags, keyed by tag name. Each tag receives its formatted chunks as the $content scoped argument, injected into the mapped element's scope.

Returns: Without $tags, the formatted message string; a key missing from the catalog (or a pattern that cannot be formatted) formats to the key itself, and engines SHOULD report missing keys during development. With $tags, an ordered sequence of text chunks and rendered tag elements.

Without $tags this expression always returns a string, so the prop MUST accept string values. With $tags it returns renderable content and is only valid in positions accepting nested elements. A tag the message uses but $tags does not map renders its chunks bare. A translation reference is only valid in value position: a top-level $t key on an element is a keyword, not a prop.

Example

{
  "type": "button",
  "props": {
    "label": {
      "$t": "form.greeting",
      "$params": { "name": { "$data": "/name" } },
      "$tags": {
        "strong": {
          "type": "text",
          "props": { "children": { "$arg": "$content" } }
        }
      }
    }
  }
}

Inline Messages

Inline messages format an ICU MessageFormat pattern carried by the document itself, without a catalog lookup:

{
  "$message": "{count, plural, one {# item} other {# items}}",
  "$params": { "count": { "$data": "/count" } }
}

Required

  • $message (string): The ICU MessageFormat pattern to format. Plurals, selects, and number and date arguments format in the active locale.

Optional

  • $params (object): Values interpolated into the pattern's arguments. Each value is itself resolved as an expression first, so data references, scoped arguments, and nested translation references are allowed.

Returns: The formatted string. A pattern that cannot be formatted (a syntax error, or a param the pattern cannot format) resolves to the pattern text itself; engines SHOULD report it during development.

This expression always returns a string. When used in a prop, the prop MUST accept string values. An inline message is not translated: text shown to users belongs in the catalog behind $t; $message composes strings from data that no catalog owns, such as URLs and other technical values.

Example

Building a link target from a data value:

{
  "type": "anchor",
  "props": {
    "children": { "$t": "product.view" },
    "href": {
      "$message": "/products/{id}",
      "$params": { "id": { "$data": "/productId" } }
    }
  }
}

On this page