ObjectStackObjectStack

Page

Page protocol schemas

Page Region Schema A named region in the template where components are dropped.

Source: packages/spec/src/ui/page.zod.ts

TypeScript Usage

import { ElementDataSourceSchema, InterfacePageConfigSchema, PageSchema, PageComponentSchema, PageComponentType, PageRegionSchema, PageTypeSchema, PageVariableSchema } from '@objectstack/spec/ui';
import type { ElementDataSource, InterfacePageConfig, Page, PageComponent, PageComponentType, PageRegion, PageType, PageVariable } from '@objectstack/spec/ui';

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

ElementDataSource

Properties

PropertyTypeRequiredDescription
objectstringObject to query
viewstringoptionalNamed view to apply
filteranyoptionalAdditional filter criteria
sort{ field: string; order: Enum<'asc' | 'desc'> }[]optionalSort order
limitintegeroptionalMax records to display

InterfacePageConfig

Interface-level page configuration (Airtable parity)

Properties

PropertyTypeRequiredDescription
sourcestringoptionalSource object name for the page
columnsstring[] | { field: string; label?: string | Record<string, string>; width?: number; align?: Enum<'left' | 'center' | 'right'>; … }[]optionalColumns shown by the page. Blank = all object fields. Defined directly on the page (no view inheritance).
sort{ field: string; order: Enum<'asc' | 'desc'> }[]optionalDefault sort order for the page, defined directly on the page.
filterBy{ field: string; operator: Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'starts_with' | … +14 more>; value?: string | number | boolean | null | (string | number)[] }[]optionalAlways-on page filter (base filter).
levelsintegeroptionalNumber of hierarchy levels to display
sourceViewstringoptional@deprecated Legacy named-view inheritance. Define columns/sort/filterBy on the page instead.
appearance{ showDescription: boolean; allowedVisualizations?: Enum<'grid' | 'kanban' | 'gallery' | 'calendar' | 'timeline' | 'gantt' | 'map' | 'chart' | 'tree'>[] }optionalAppearance and visualization configuration
userFilters{ element: Enum<'dropdown' | 'tabs' | 'toggle'>; fields?: object[]; tabs?: object[]; showAllRecords?: boolean; … }optionalEnd-user quick-filter bar for this page (overrides the source view's userFilters)
userActions{ sort: boolean; search: boolean; filter: boolean; refresh: boolean; … }optionalUser action toggles
addRecord{ enabled: boolean; position: Enum<'top' | 'bottom' | 'both'>; mode: Enum<'inline' | 'form' | 'modal'>; formView?: string }optionalAdd record entry point configuration
buttonsstring[]optionalToolbar buttons — names of the source object's actions to surface in the page toolbar
recordActionEnum<'drawer' | 'page' | 'modal' | 'none'>optionalHow clicking a record opens its detail (drawer | page | modal | none). Default: drawer
showRecordCountbooleanoptionalShow record count at page bottom
allowPrintingbooleanoptionalAllow users to print the page

Page

Properties

PropertyTypeRequiredDescription
namestringPage unique name (lowercase snake_case)
labelstring | Record<string, string>Display label — the default-language string, or an inline locale map ({ en, "zh-CN" }) resolved at render time
descriptionstring | Record<string, string>optionalDisplay label — the default-language string, or an inline locale map ({ en, "zh-CN" }) resolved at render time
iconstringoptionalPage icon name
typeEnum<'record' | 'home' | 'app' | 'utility' | 'list'>optionalPage type
variables{ name: string; type?: Enum<'string' | 'number' | 'boolean' | 'object' | 'array' | 'record_id'>; defaultValue?: any; source?: string }[]optionalLocal page state, exposed to expressions as page.<name> and writable by interactive elements via source (master/detail, filtered dashboards).
objectstringoptionalBound object (for Record pages)
templatestringoptionalLayout template name (e.g. "header-sidebar-main")
regions{ name: string; width?: Enum<'small' | 'medium' | 'large' | 'full'>; components: object[] }[]optionalLayout regions (header, main, sidebar, footer) with their components. Optional — list pages use interfaceConfig, slotted pages use slots, and an empty full page falls back to the synthesized default layout.
isDefaultbooleanoptional
assignedProfilesstring[]optional
interfaceConfig{ source?: string; columns?: string[] | object[]; sort?: object[]; filterBy?: object[]; … }optionalInterface-level page configuration (for Airtable-style interface pages)
aria{ ariaLabel?: string | Record<string, string>; ariaDescribedBy?: string; role?: string }optionalARIA accessibility attributes
kindEnum<'full' | 'slotted' | 'html' | 'react' | 'jsx'>optionalPage override mode. full | slotted = structured authoring; html = author-written constrained JSX/HTML+Tailwind compiled (parsed, never executed) to the tree (ADR-0080; the legacy value 'jsx' is a deprecated alias); react = real-React source executed at render by the runtime (ADR-0081); it runs author JS, so it is gated by a host capability that defaults ON and is disabled server-side via the OS_PAGE_REACT=off env toggle.
slots{ header?: object | object[]; actions?: object | object[]; alerts?: object | object[]; highlights?: object | object[]; … }optionalSlot override map for slotted pages
sourcestringoptionalPage source text. For kind==='html' (alias 'jsx') it is constrained JSX/HTML+Tailwind compiled to the tree by @objectstack/sdui-parser at save time (parse, never execute). For kind==='react' it is real React/JSX executed at render by @object-ui/react-runtime (trusted tier). Authoritative over regions in both.
requiresstring[]optionalPlugin namespaces the JSX source references (validated at save and load)
_lockEnum<'none' | 'no-overlay' | 'no-delete' | 'full'>optionalItem-level lock — controls overlay & delete (ADR-0010).
_lockReasonstringoptionalHuman-readable reason shown when a write is refused by _lock.
_lockSourceEnum<'artifact' | 'package' | 'env-forced'>optionalLayer that set _lock (artifact | package | env-forced).
_provenanceEnum<'package' | 'org' | 'env-forced'>optionalOrigin of the item (package | org | env-forced).
_packageIdstringoptionalOwning package machine id.
_packageVersionstringoptionalOwning package version.
_lockDocsUrlstringoptionalOptional documentation link surfaced next to _lockReason.

PageComponent

Properties

PropertyTypeRequiredDescription
typeEnum<'page:header' | 'page:footer' | 'page:sidebar' | 'page:tabs' | 'page:accordion' | 'page:card' | 'page:section' | 'record:details' | 'record:highlights' | 'record:related_list' | 'record:activity' | 'record:chatter' | 'record:path' | 'record:alert' | 'record:quick_actions' | 'record:reference_rail' | 'record:history' | 'app:launcher' | 'nav:menu' | 'nav:breadcrumb' | 'global:search' | 'global:notifications' | 'user:profile' | 'ai:chat_window' | 'ai:suggestion' | 'element:text' | 'element:number' | 'element:image' | 'element:divider' | 'element:button' | 'element:filter' | 'element:form' | 'element:record_picker' | 'element:text_input'> | stringComponent Type (Standard enum or custom string)
idstringoptionalUnique instance ID
labelstring | Record<string, string>optionalDisplay label — the default-language string, or an inline locale map ({ en, "zh-CN" }) resolved at render time
propertiesRecord<string, any>optionalComponent props passed to the widget. See component.zod.ts for schemas.
eventsRecord<string, string>optionalEvent handlers map
styleRecord<string, string>optionalInline styles or utility classes
classNamestringoptionalCSS class names
responsiveStyles{ large?: Record<string, string | number>; medium?: Record<string, string | number>; small?: Record<string, string | number>; xsmall?: Record<string, string | number> }optionalPer-breakpoint scoped style maps (ADR-0065)
visibleWhenstring | { dialect: Enum<'cel' | 'cron' | 'template'>; source?: string; ast?: any; meta?: object }optionalVisibility predicate (CEL) — component rendered only when TRUE. Binds record, current_user, page.<var>. e.g. "page.selectedProjectId != ''"
visibilitystring | { dialect: Enum<'cel' | 'cron' | 'template'>; source?: string; ast?: any; meta?: object }optional[DEPRECATED → visibleWhen] Visibility predicate (CEL). Normalized to visibleWhen at parse.
dataSource{ object: string; view?: string; filter?: any; sort?: object[]; … }optionalPer-element data binding for multi-object pages
responsive{ breakpoint?: Enum<'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'>; hiddenOn?: Enum<'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'>[]; columns?: object; order?: object }optionalResponsive layout configuration
aria{ ariaLabel?: string | Record<string, string>; ariaDescribedBy?: string; role?: string }optionalARIA accessibility attributes

PageComponentType

Allowed Values

  • page:header
  • page:footer
  • page:sidebar
  • page:tabs
  • page:accordion
  • page:card
  • page:section
  • record:details
  • record:highlights
  • record:related_list
  • record:activity
  • record:chatter
  • record:path
  • record:alert
  • record:quick_actions
  • record:reference_rail
  • record:history
  • app:launcher
  • nav:menu
  • nav:breadcrumb
  • global:search
  • global:notifications
  • user:profile
  • ai:chat_window
  • ai:suggestion
  • element:text
  • element:number
  • element:image
  • element:divider
  • element:button
  • element:filter
  • element:form
  • element:record_picker
  • element:text_input

PageRegion

Properties

PropertyTypeRequiredDescription
namestringRegion name (e.g. "sidebar", "main", "header")
widthEnum<'small' | 'medium' | 'large' | 'full'>optional
components{ type: Enum<'page:header' | 'page:footer' | 'page:sidebar' | 'page:tabs' | 'page:accordion' | … +29 more> | string; id?: string; label?: string | Record<string, string>; properties?: Record<string, any>; … }[]Components in this region

PageType

Page type — the page KIND. Only types with a dedicated renderer are authorizable; visualizations of a list page live in interfaceConfig, not here.

Allowed Values

  • record
  • home
  • app
  • utility
  • list

PageVariable

Properties

PropertyTypeRequiredDescription
namestringVariable name. Exposed to expressions as page.<name>.
typeEnum<'string' | 'number' | 'boolean' | 'object' | 'array' | 'record_id'>
defaultValueanyoptionalInitial value. Defaults to a type-appropriate empty value when omitted.
sourcestringoptionalComponent id that writes this variable (e.g. an element:record_picker whose id matches).

On this page