Skip to content

Context, State & Value Forms

AspectPublic keysPrivate keys
NamingAny key NOT prefixed with _Keys starting with _ (e.g. _data, _source, _label)
InheritanceInherited by all children down the render tree. Children may override.Scoped to the declaring component. Not passed to children.
PurposePass data and parameters down the component tree.Internal wiring and runtime-reserved properties.
ResolutionInherited from ancestors; children may override.Overrides the inherited public key for this component only.
FormScopeDescriptionExample
${path}StateReads from the current module’s state only (module-local; no cross-module resolution). Supports dot-notation and bracket indexing (see §6.2.1).${user.email}, ${items[0]}
@{path}ContextReads from the current component’s context chain, including inherited public keys. Same path syntax as ${}.@{item.title}, @{item.tags[0]}
{{ expr }}ExpressionA computed value evaluated at runtime. Can reference state and context.{{ empty(${q}) }}
#{key}SystemA runtime-provided value such as the current timestamp or user identifier.#{now}, #{user}

ℹ Note: Plain state and context references — ${key} and @{key} — are valid expression values on their own. The {{ }} wrapper is required only when the value contains operators, function calls, or mixed content.

⚠ Important: a string value is parsed as exactly one form. At any field that accepts a value, the runtime parses a string as one of three mutually-exclusive forms:

  1. a literal string — the default. The runtime does not scan for and substitute markers inside literals.
  2. a single bare reference — the whole value is one of "${path}", "@{path}", or "#{key}" with no surrounding content.
  3. a wrapped expression — the whole value is "{{ … }}".

Anything else — two references side by side, a reference adjacent to literal text, an unfinished expression — falls into form (1) and renders verbatim. To compose values from multiple parts, wrap the whole value in {{ … }} and join with + (string concatenation, see §8.3).

Examples:

Goal✅ Right❌ Wrong (parsed as literal form, renders verbatim)
Single ref alone"_text": "@{item.name}"
Literal + ref"_text": "{{ 'Hi ' + @{item.name} }}""_text": "Hi @{item.name}"
Two refs joined"_text": "{{ @{a} + ' ' + @{b} }}""_text": "@{a} @{b}"
Ref + system value"_text": "{{ @{item.name} + ' · ' + cast(#{now}, 'str') }}""_text": "@{item.name} · #{now}"

⚠ Markers do not combine. The four marker prefixes — ${, @{, #{, {{ — are mutually exclusive. There is NO merged form. AI authoring sometimes invents @${path}.field thinking it means “context via state” — it does not; it means nothing. The runtime parses @$ as literal text and the string renders verbatim, curly braces and all. Pick one marker per reference based on where the value lives:

// ✅ Correct — one marker per reference
"_text": "{{ ${forecast.temperature} }}" // state path
"_text": "{{ @{forecast.temperature} }}" // context path
"_text": "@{forecast.temperature}" // bare context ref
"_text": "${forecast.temperature}" // bare state ref
// ❌ Invented merged prefixes — render as literal text
"_text": "{{ @${forecast}.temperature }}" // `@$` is not a marker
"_text": "{{ ${@{forecast}}.temperature }}" // nested markers are not a valid form
"_text": "@${forecast.temperature}" // same — `@$` is not a marker

A path inside ${...} or @{...} is a dot-separated, bracket-indexable chain of segments that walks the resolved value.

SegmentSemanticsExample
.nameDictionary key lookup. Returns .none if the current value is not a dictionary or the key is absent.${user.profile.email}
[n]Array index. Returns .none if the current value is not an array or the index is out of bounds.${items[0]}, ${items[2]}
[-n]Tail-index on arrays — -1 is the last element, -2 the second to last, and so on.${items[-1]}
[key]Integer literals index arrays; string literals wrapped in single quotes index dictionary keys.${d['color']}
[expr]Any expression that resolves to an integer (array index) or string (dict key).${items[${i} + 1]}
[predicate]Filter predicate (array only) — the brackets contain the predicate directly (no extra delimiters); keeps elements where it resolves to truthy. See §8.4 for full operator support.${web[title ~ ${search}]}, ${items[done == true]}, ${entries[at > #{now} - 604800]}

Segments compose freely: ${order.items[0].name}, @{item.video_files[-1].link}.

Null propagation. If any segment resolves to .none or a type-incompatible value, the entire path resolves to .none — it does not raise an error (see §8.8 Evaluation Rules).

Filter-predicate location & ref support. The filter predicate [predicate] is a path-only construct: it is recognized inside ${…} / @{…} path segments, NOT as a postfix on an arbitrary expression. ${devices}[room ~ "living"] (outside the ${}) is parsed as an expression subscript and will NOT filter — see §8.3. Always write filters inside the path: ${devices[room ~ "living"]}.

Predicate shape. Inside the brackets, the runtime evaluates the content as an expression against each array element. The element’s dictionary becomes the context, so each unprefixed identifier (e.g. title, hasMood, takenAt) resolves to that key on the current element. Any operator from §8.3 / §8.4 works:

UseExample
Substring / element-of / has-key (~)${list[title ~ ${search}]} — keeps items whose title contains ${search}
Equality / inequality (==, !=)${list[id != ${target}]}, ${entries[hasMood == true]}
Numeric comparison (<, <=, >, >=)${entries[at > #{now} - 604800]} (last week), ${entries[score >= 7]}
Compound (&&, `
Chained filters${entries[score > 7][hasMood == true]} — each […] filters the result of the previous

val (the right side of any comparison) can be a literal, a ${state} ref, a #{system} value, or an expression. @{context} refs inside a filter may not resolve — see the note above.

Crucial: ~ is contains, not equality. Using ~ against a primitive field (hasMood ~ true, score ~ 5) is type-incompatible — the predicate resolves to .none (falsy) for every element and the filter returns empty. Use == for equality of primitives.

Inside the filter value, use a state reference (${…}) or a literal. Context references (@{…}) inside filter values are not guaranteed to resolve — the filter is evaluated in a scope where @{item} / @{index} bindings from an enclosing dynamic prototype may not be visible, and treating them as .none (matches nothing) is a valid runtime implementation. To filter by an item from an outer prototype, capture that item’s field into a state key first (canonical pattern: push to a module destination whose onAppear does the capture; see §link), then filter via ${capturedId}.

KeyTypeDescription
#{now}dateCurrent instant. Resolved freshly each time the expression is evaluated.
#{user}stringIdentifier for Vendor (IDFV) — a UUID unique to the app vendor on this device. Not a user identity. Resets if all apps from the vendor are uninstalled.
#{locale}stringLocale identifier for the device (e.g. “en_US”). Note: uses underscore separator, not hyphen.
#{orientation}stringDevice orientation: “portrait” or “landscape”.
#{today}dateMidnight at the start of today.
#{yesterday}dateMidnight at the start of yesterday.
#{tomorrow}dateMidnight at the start of tomorrow.
#{endofweek}dateStart of the last day of the current week.
#{startofmonth}dateFirst day of the current month, midnight.
#{endofmonth}dateLast second of the current month (23:59:59).
#{language}stringLanguage code of the device locale (e.g. “en”).
#{currency}stringISO 4217 currency code for the device locale (e.g. “USD”).
#{timezone}stringIANA timezone identifier (e.g. “Europe/Kyiv”).
#{device_type}stringDevice form factor: phone
#{device}stringDevice model name (e.g. “iPhone”).
#{systemname}stringOS name (e.g. “iPhone OS”).
#{systemversion}stringFull OS version string (e.g. “Version 17.4 (Build 21E219)”).
#{is_split_view}booltrue when an iPad app is running in Split View or Slide Over. Always false on non-iPad.
#{horizontal_size_class}stringHorizontal size class of the active window: “regular” (iPad full-screen / split-screen wide) or “compact” (iPhone / iPad Slide Over / split-screen narrow).

A date value behaves as a date inside expressions and paths — it can be compared with other dates, passed to cast, sorted, etc. When a date value crosses into a JSON sink (a repo body, a remote request body, a host trigger payload, any state that is later serialised), it is flattened to a double carrying Unix epoch seconds since 1970. On the way back out (e.g. read from a repo) it is reconstituted as a double, not as a date — once stored, a date is just a number, and downstream consumers must treat it as such.

Cast behaviour for dates:

CastResult
cast(d, 'str')Locale-formatted display string (e.g. "5/21/2026, 4:14 PM" in en_US). Locale-dependent, minute-precision in most locales. Useful for display only — not a stable machine identifier.
cast(d, 'int')Returns 0. No int conversion is implemented for dates today; use the boundary-crossing path above to obtain the epoch number.
cast(d, 'date')No-op (returns the same date).
SchemeResolves to
dp://A dependency registered in the current module’s dependency array.
sf://A system icon identified by name. On iOS / macOS the runtime resolves to the corresponding SF Symbol; on other platforms the runtime MAY substitute a visually equivalent icon from its native set (e.g. Material Symbols, Fluent Icons) or fall back to the platform’s default missing-icon placeholder. Authors SHOULD treat sf:// names as portable icon identifiers rather than Apple-only assets.

⚠ Important: scheme prefixes are required. A value supplied to any source-accepting field (_source, _image, _placeholder, _tabImage excepted — see below, and other fields whose contract is “a source string”) MUST carry the <scheme>:// prefix shown above. The runtime does not infer a scheme from context — a string without a prefix is treated as a literal. The single documented exception is the _tabImage context key on tab children, where a bare SF Symbol name is accepted for ergonomic reasons. | asset:// | A bundled app asset (image, file, etc.). | | file:// | A file in the current StemJSON zip package. | | rs:// | A named resource bundled into the host application binary (not the zip package). | | l10n:// | A localization string key. Resolved from packaged *.strings files. | | http(s)://… | A remote URL (image loading, remote template loading, etc.). |