Owning object — required for object-scoped kinds: state_machine is a rule ON an object (ADR-0020), permission renders a matrix FOR one; omit for top-level flow
mode
Enum<'diagram' | 'matrix' | 'summary'>
optional
Render form; defaults per type (diagram for flow/state_machine, matrix for permission)
detail
Enum<'business' | 'technical'>
✅
Authoring altitude (ADR-0051 §3.4): business collapses technical flow nodes to business steps + approvals. NOT access (cf. book.audience); permission projection is automatic and render-time, never set here
Field whose value is written into the bound page variable (default id)
label
string | Record<string, string>
optional
Control label rendered above the select
filter
any
optional
Filter criteria for available records
sort
{ field: string; order: Enum<'asc' | 'desc'> }[]
optional
Row order — synonym of the component-level dataSource.sort, which takes precedence when both are set
limit
integer
optional
Max records offered — synonym of the component-level dataSource.limit, which takes precedence when both are set (renderer default 50)
targetVariable
string
optional
Page variable to bind selected record ID(s)
placeholder
string | Record<string, string>
optional
Placeholder text
emptyText
string | Record<string, string>
optional
Text shown when the query returns no records (default "No records")
displayField
never
optional
[REMOVED] element:record_picker property displayField was removed in @objectstack/spec 17.0.0 (#5775, ADR-0087 D2) — it was a required declaration no renderer ever read, while the renderer honoured labelField for the same thing and defaulted to name. Rename the key to labelField; the value (a field name) is unchanged. Run os migrate meta --from 16 to rewrite existing sources automatically.
searchFields
never
optional
[REMOVED] element:record_picker property searchFields was removed in @objectstack/spec 17.0.0 (#5775, ADR-0049) — the picker renders a plain single-select with no search input, so no renderer ever read it and it narrowed nothing. Delete the key. To restrict which records the picker offers, use filter (or the component-level dataSource.filter), which the query path does apply. Run os migrate meta --from 16 to rewrite existing sources automatically.
multiple
never
optional
[REMOVED] element:record_picker property multiple was removed in @objectstack/spec 17.0.0 (#5775, ADR-0049) — the picker is a single-select Select and the bound page variable holds one record id, so multiple: true selected nothing extra and reported success. Delete the key; multi-record selection is not implemented on this element. Run os migrate meta --from 16 to rewrite existing sources automatically.
Native input type — drives keyboard/validation affordance and how the bound value is coerced (number → numeric).
label
string | Record<string, string>
optional
Field label shown above the input
placeholder
string | Record<string, string>
optional
Placeholder text shown when empty
defaultValue
string | number
optional
Initial value; seeds the bound page variable on mount
required
boolean
✅
Mark the field as required
disabled
boolean
✅
Disable the input
description
string | Record<string, string>
optional
Helper text shown below the input
targetVariable
string
optional
Page variable this input writes to. Declarative hint; the live binding resolves via the variable whose source equals this component id (see PageVariableSchema).
Display label — the default-language string, or an inline locale map ({ en, "zh-CN" }) resolved at render time
bordered
boolean
✅
actions
never
optional
[REMOVED] page:card property actions was removed in @objectstack/spec 17.0.0 (#6946, ADR-0087 D2) — no renderer ever read it: objectui's card renderer builds its <Card> from title, bordered, children and footer only, has no actions area, and the component registry never published it as an input, so an authored value was accepted and dropped. Delete the key and author the buttons as components in the card's children or footer (element:button, record:quick_actions), which is what actually renders. Run os migrate meta --from 16 to rewrite existing sources automatically.
children
any[]
optional
Card content components, in order (the card body slot)
body
never
optional
[REMOVED] page:card property body was removed in @objectstack/spec 17.0.0 (#5775, ADR-0087 D2) — it was a second spelling of the composition slot every other container calls children, and the renderer reads both. Rename the key to children; the value (an array of child components) is unchanged. Run os migrate meta --from 16 to rewrite existing sources automatically.
Page title. Omit to let the renderer derive the heading from the record (the default for record pages) — set explicitly on non-record pages (dashboard, landing) with no record to derive from.
subtitle
string | Record<string, string>
optional
Page subtitle
icon
never
optional
[REMOVED] page:header property icon was removed in @objectstack/spec 17.0.0 (#6946, ADR-0087 D2) — no renderer ever read it: objectui resolves icon only per header action (action.icon), never off the header's own props bag, and the component registry never published it as an input, so an authored value was accepted and dropped. Delete the key. The header's own identity is drawn by the record chrome (recordChrome, on by default) and each action carries its own icon. Run os migrate meta --from 16 to rewrite existing sources automatically.
breadcrumb
boolean
✅
Show breadcrumb
actions
string[]
optional
Action IDs to show in header
recordChrome
boolean
✅
Render the record chrome — the title as a record chip with its follow star and copy-id button. Set false on a non-record page (dashboard, landing) to fall back to the bare heading layout.
showStar
boolean
✅
Show the follow (favourite) star beside the record title. Part of the record chrome — no effect when recordChrome is false.
showCopyId
boolean
✅
Show the copy-record-id button beside the record title. Part of the record chrome — no effect when recordChrome is false.
maxVisible
integer
optional
How many header actions render as inline buttons before the rest fold into the overflow menu (renderer default 3).
mobileMaxVisible
integer
optional
The maxVisible budget on mobile viewports (renderer default 1).
Tab-strip visual style: 'line' underlines the active tab, 'card' frames each tab, 'pill' renders rounded pills
type
never
optional
[REMOVED] page:tabs property type was removed in @objectstack/spec 17.0.0 (#6776, ADR-0087 D2) — a props key named type collides with the page component's own dispatch key, so it is unauthorable in the flat and JSX carriers and was never validated in them. Rename the key to tabStyle; the value (line | card | pill) is unchanged. Run os migrate meta --from 16 to rewrite existing sources automatically.
position
Enum<'top' | 'left'>
optional
alwaysShowStrip
boolean
optional
Render the tab strip even when only one tab is visible (renderer default: a one-tab strip is hidden).
[REMOVED] record:details property layout was removed in @objectstack/spec 17.0.0 (#6946, ADR-0087 D2) — its declared auto | custom semantics were never implemented: the renderer tests layout only against inline | compact, two values the schema never permitted, so both legal values took the same branch and the key selected nothing. Delete the key — the body is already chosen by what you author: sections renders the explicit groups (the old custom), and omitting it falls back to the object's highlightFields (the old auto). Run os migrate meta --from 16 to rewrite existing sources automatically.
Field groups rendered as the detail body, in order. Object form: { name?, label?, columns?, fields }.
fields
string[]
optional
Explicit field list to display (optional, overrides highlightFields)
hideFields
string[]
optional
Field names to omit from the body — applied to fields and to every section's fields (used to dedupe fields already shown in record:highlights or as the page title)
inlineEdit
boolean
optional
Allow inline field editing in the detail body (renderer default: on, where the object itself is editable — set false to force it off).
showHeader
boolean
optional
Render the detail body's own heading (renderer default: off).
Render this chip read-only — suppresses inline editing on the highlight card. Use for hook/automation-maintained columns that must not be hand-edited from the record header.
Key fields to highlight (1-7 fields max, typically displayed as prominent cards). Each item may be a bare field name or {name, label?, icon?, type?, readonly?} for inline overrides.
Field on related object that points to this record (e.g., "account_id")
relationshipValueField
string
✅
Parent-record field whose value relationshipField stores (default 'id'; e.g. 'name' for name-keyed junctions).
columns
string[]
optional
Fields to display in the related list. Optional: when omitted, columns derive from the related object's highlightFields / default list columns (a related list is just another surface that lists that object). Override chain: child highlightFields → field-level relatedListColumns → this inline list.