ObjectStackObjectStack

Expression Bindable Text Keys

Expression Bindable Text Keys protocol schemas

Expression-bindable text keys — the CLOSED vocabulary of top-level text keys a SchemaRenderer evaluation memo evaluates, and the per-component carriage map that says which component types carry which of them.

Provenance (not re-litigable here)

objectui#4795 measured the hole: apart from content, no top-level text key on a rendered SDUI node was BOTH evaluated by the renderer's expression memo AND read back by the component renderer — statistic.value: '${data.n}' rendered the literal ${data.n}, and the props-envelope workaround rendered blank. The 2026-08-17 maintainer ruling on that card deferred the fix (Direction 1) behind a restart condition and pre-defined its terms, verbatim: "the key set is declared in @objectstack/spec/types as a closed enum, never inferred". On 2026-08-18 the maintainer ruled the restart condition met by product intent (a metadata-authored live dashboard is a basic requirement), reopening Direction 1 on those terms. This module is the spec half (objectstack#9599); the objectui half extends the evaluation memo to CONSUME these exports (it rides objectui#4795) instead of hard-coding a twin list.

Related, and deliberately out of this module's reach:

  • ⛔ Direction 2 (merging the props envelope into the node) is permanently rejected (same 08-17 ruling) — nothing here re-legalizes the envelope shape the objectui#4786 teaching rewrite retired.
  • content is NOT a member. It already has its own evaluation leg in the memo and its own read-back contract; adding it here would give one key two declared evaluation paths.
  • The properties / props config bags are evaluated per-value by their own memo legs (objectui#4799 / #5122) — this vocabulary is only about keys authored at the NODE'S TOP LEVEL.

The contract

For a component type with a row in EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT, the renderer's evaluation memo evaluates exactly the listed keys (when the authored value is an expression-bearing string); every other key stays inert text. For a component type with NO row, the answer is the empty set — closed and mechanically answerable in both directions, never inferred from what a renderer happens to read.

Why these rows (measured, not inferred)

Rows are a RECORD of what each component renderer already reads back from the node's top level, measured at the .objectui-sha pin 82a9417 (re-verified identical at objectui origin/main 6c68b13 on 2026-08-18) — declaring a key a renderer does not read back would recreate the evaluated-but-blank half of the objectui#4795 table:

typerenderer read points (top level, within the closed set)
statisticschema.label, schema.value, schema.description (data-display/statistic.tsx)
cardschema.title, schema.description (layout/card.tsx)
buttonschema.label (form/button.tsx, action/action-button.tsx)

These are the measured motivating cases from objectui#4795 (dashboard workhorses). Other registered renderers also read keys from this closed set at the top level (alert/empty/dialog title+description, badge label, form inputs' value/label, …) — those rows are deliberately NOT declared yet: form-control value is interactive state rather than display text, and each row is an accept-surface widening that should arrive with its own measurement, not ride this one (startup scope discipline). Adding a row is additive and spec-first; do it here, never as a renderer-side inference.

Source: packages/spec/src/ui/expression-bindable-text-keys.zod.ts

TypeScript Usage

import { ExpressionBindableTextKeySchema } from '@objectstack/spec/ui';
import type { ExpressionBindableTextKey } from '@objectstack/spec/ui';

// Validate data
const result = ExpressionBindableTextKeySchema.parse(data);

ExpressionBindableTextKey

One of the closed set of expression-bindable text keys — the top-level node keys a SchemaRenderer evaluation memo may evaluate (objectui#4795 Direction 1; carriage per component type is EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT).

Allowed Values

  • title
  • label
  • value
  • description

On this page