Skip to main content
View as Markdown

The Form Component

form — one type, two modes: table-bound writes through the records API, static submits to a declared form or to a URL of your own.

dataSource is what decides the mode. Declare one and the form is table-bound: its fields resolve against that table's columns, and submitting writes a record through the records API. Omit it and the form is static: it collects the fields declared on it and submits them to a form you declared in forms[] (formRef) or to a URL you name (endpoint). Nothing else about the component changes.

Path Kind Values Default Description
formRef string Reference a top-level form by name (app.forms[].name). Renders that form inline.
layout enum single-column, two-column, custom Form layout mode: single-column | two-column | custom

dataSource

Path Kind Values Default Description
dataSource object Binds a component to table data. Supports list, single-record, and search modes with filtering, sorting, and pagination.
dataSource.table string Table to bind to: a declared name (validated against app.tables), or a $param.<name> route reference declared by the page path
dataSource.fields array Specific fields to fetch from the table
dataSource.fields[] string One field name, spelled as the bound table declares it
dataSource.mode enum list, single, search Data fetching mode: 'list' (multiple), 'single' (one record), 'search' (interactive)
dataSource.filter array Filter conditions applied with AND logic
dataSource.filter[] object (truncated) Single filter condition for data source queries
dataSource.sort array Sort rules applied in order
dataSource.sort[] object (truncated) Single sort rule for data source queries
dataSource.pagination object Pagination configuration for data source
dataSource.pagination.pageSize number Number of records per page
dataSource.pagination.style enum numbered, loadMore, infinite How pagination controls are displayed (default: numbered). infinite is accepted but not implemented and pages as numbered.
dataSource.param string Route parameter name for single mode (e.g., slug, id)
dataSource.searchEngine enum client, fts, trigram, hybrid Search backend for this data source (default: 'client'). Only 'client' is dispatched today; the other three validate and search as 'client' does.
dataSource.searchFields array Fields to search across in search mode
dataSource.searchFields[] string One field name the search term is matched against
dataSource.debounceMs number Debounce delay for search input (ms)
dataSource.limit number Maximum number of results to return
dataSource.targetId string Publisher-side identifier for cross-component references — addressable by a FilterAction (targetDataSource) and by a sibling subscriber's bindTo (shared filter/period state)
dataSource.bindTo string ID of a publisher component whose value drives this data source (cross-component binding). By default a search-input whose query string drives the search; when sharedFilter is also set, a shared filter/period selector whose published params are merged into every request
dataSource.sharedFilter object Companion to bindTo: the bound publisher is a shared filter/period selector whose published params are merged into every request this data source issues. One selector can drive many sibling subscribers. Inert without bindTo.
dataSource.sharedFilter.params array (truncated) Request-param keys this subscriber consumes from the shared publisher's value bag (omit to merge the full bag verbatim)
dataSource.refreshMode enum none, poll, realtime Data refresh strategy for this binding (default: 'none'). 'poll' uses pollIntervalMs; 'realtime' subscribes to live change events.
dataSource.pollIntervalMs number 30000 Polling interval in milliseconds for refreshMode: poll (min 1000, max 300000). Defaults to 30000 when omitted, and is ignored unless refreshMode is 'poll'.

autoSave

Path Kind Values Default Description
autoSave object Configuration for automatic persistence of edits. Applies to table, form, kanban, and calendar components.
autoSave.saveMode enum auto, onBlur, manual Save trigger strategy: 'auto' (debounced), 'onBlur' (field blur), 'manual' (button). Default: 'manual'.
autoSave.autoSaveDebounceMs number Debounce delay for auto-save in milliseconds (default: 500, min: 100)
autoSave.showSaveIndicator boolean Display a save status indicator (Saving... / Saved / Error). Default: true when saveMode is auto or onBlur.
autoSave.saveIndicatorPosition enum inline, toast, toolbar Where the save status indicator appears
Path Kind Values Default Description
search object Shared search bar configuration for data-bound components (table, kanban, calendar)
search.enabled boolean Enable search bar (default: true)
search.placeholder string Search input placeholder text
search.debounceMs number Debounce delay for search input in ms (default: 300)
search.highlight boolean Highlight matched search terms in results (default: false)

wizard

Path Kind Values Default Description
wizard object Multi-step wizard configuration. Splits form fields into sequential steps with Next/Back navigation.
wizard.steps array Ordered list of wizard steps
wizard.steps[] object (truncated) One step of the wizard: its label, and the fields it collects

inlinePrefill

Path Kind Values Default Description
inlinePrefill object Auto-prefill relationship/scalar fields on an embedded form using values from the host page record.
inlinePrefill.prefill object Map of form-field column name to prefill value. Supports $parent.<field> tokens that resolve against the host page record.
inlinePrefill.lockPrefill boolean When true, prefilled fields render as hidden inputs and the server revalidates the parent on submit (returns 422 if the parent is gone).

endpoint

Path Kind Values Default Description
endpoint object Custom-endpoint submit target for a form: POST collected field values as JSON to an arbitrary url, with response-envelope tolerance and the shipped onSuccess effects (status + sibling refetch).
endpoint.url string Custom submit URL (any path; not the records API). e.g. /api/auth/admin/create-user
endpoint.method enum POST, PUT, PATCH HTTP method for the custom-endpoint submit (defaults to POST)
endpoint.responseEnvelope enum sovrium, better-auth, raw sovrium Response-envelope interpretation: sovrium (default), better-auth (always-200 enumeration-safe envelope at /api/auth/admin/*), raw (status-only, no body assumptions)
endpoint.submitLabel string Submit button label (defaults to the form submit label)
endpoint.submitVariant enum default, destructive, outline, secondary, ghost, link, fab Visual weight of the submit button, from the platform button vocabulary (the same members a button component accepts). Omit for the primary 'default' fill, unchanged. Set 'secondary' when a page stacks several small forms and a column of primary buttons would make every row look like the page's main action.
endpoint.onSuccess object Success handler for a fetch action: the toast slot plus optional client-state effects — a persistent inline status region (status), a sibling data-bound refetch (refetch), and a full-page reload (reload) that recomposes the page server-side. reload is mutually exclusive with status and refetch.
endpoint.onSuccess.type enum toast What the component does once the action returns — navigate away, reset the form, show a message or a success page, send the reader to their role landing, or raise a toast.
endpoint.onSuccess.message string Toast notification message. Supports $variable references.
endpoint.onSuccess.variant enum default, success, destructive, error, warning, info Visual style of the toast notification
endpoint.onSuccess.duration number Auto-dismiss duration in milliseconds (default: 5000)
endpoint.onSuccess.actionLabel string Label of an optional action button rendered inside the toast
endpoint.onSuccess.actionUrl string URL invoked (POST) when the toast action button is clicked. Required with actionLabel.
endpoint.onSuccess.status object (truncated) A persistent inline role="status" region populated on success (the persistent counterpart to a transient toast).
endpoint.onSuccess.refetch string | array (truncated) props.id (or array of ids) of sibling data-bound component(s) to re-query on success. Works for both a DB-table dataSource and a dataSource.system read endpoint.
endpoint.onSuccess.reload boolean When true, the browser reloads the page after a successful request so the SERVER recomposes it — the effect refetch cannot express, because refetch re-queries one region and skips any region holding a mounted island. Use it when the request changes something the server read at render time (the active language, the chrome). Refused at decode alongside status or refetch (both are same-page effects the reload subsumes); the required toast message is NOT displayed, because the reload replaces the document that would have shown it.
endpoint.onError object Toast notification rendered after a fetch action completes
endpoint.onError.type enum toast What the component does once the action returns — navigate away, reset the form, show a message or a success page, send the reader to their role landing, or raise a toast.
endpoint.onError.message string Toast notification message. Supports $variable references.
endpoint.onError.variant enum default, success, destructive, error, warning, info Visual style of the toast notification
endpoint.onError.duration number Auto-dismiss duration in milliseconds (default: 5000)
endpoint.onError.actionLabel string Label of an optional action button rendered inside the toast
endpoint.onError.actionUrl string URL invoked (POST) when the toast action button is clicked. Required with actionLabel.

fields

Path Kind Values Default Description
fields array Per-field configuration for form component (labels, placeholders, visibility)
fields[] object Per-field configuration for a form component
fields[].field string Field identifier: a table column name (table-bound form) OR the JSON body key (endpoint-bound form)
fields[].control enum text, email, password, number, tel, url, textarea, select Explicit input control for an endpoint-bound form field (text/email/password/number/tel/url/textarea/select). Omitted for table-bound forms (control derived from the column type).
fields[].options array (truncated) Dropdown options for a control: select field ({ value, label? })
fields[].optionsSource object (truncated) Dynamic option source for a choice control: table rows, or the rows of a system read endpoint
fields[].label string Custom label text (overrides default field name)
fields[].description string Guidance text rendered beside the control and linked via aria-describedby (overrides the bound field's description). Required to describe a control on an endpoint-bound form, which has no table field schema to resolve from. Unlike a placeholder it persists once the user starts typing.
fields[].placeholder string Placeholder text shown when field is empty
fields[].readOnly boolean If true, field is displayed but not editable
fields[].disabled boolean If true, field input is disabled
fields[].defaultValue string | number | boolean Default value for create mode. Supports static values or $variable references.
fields[].hidden boolean If true, field value is submitted but input is not rendered
fields[].visibleWhen object (truncated) Condition supporting simple comparisons and compound OR/AND logic for field visibility, required, and disabled states
fields[].requiredWhen object (truncated) Condition supporting simple comparisons and compound OR/AND logic for field visibility, required, and disabled states
fields[].disabledWhen object (truncated) Condition supporting simple comparisons and compound OR/AND logic for field visibility, required, and disabled states
fields[].accept string Comma-separated MIME types or extensions (e.g. "image/*,.pdf")
fields[].dropZone boolean If true, renders a drag-and-drop area for file uploads
fields[].maxFiles number Maximum number of files allowed (for multiple-attachments fields)

fieldGroups

Path Kind Values Default Description
fieldGroups array Groups form fields under labeled section dividers
fieldGroups[] object Groups form fields under a labeled section divider
fieldGroups[].label string Group label displayed as a section divider above the fields
fieldGroups[].fields array (truncated) Array of field names belonging to this group

dataSource is table-only by design, since writes go to a table and never to a read endpoint; mode: single supplies current values for an edit form. layout is single-column by default, with two-column and custom beside it. fieldGroups divides the form into { label, fields } sections.

app.yaml
tables:
  - name: contacts
    fields:
      - { name: email, type: email }
      - { name: notes, type: long-text }
pages:
  - name: Contact
    path: /contacts/:id
    components:
      - type: form
        dataSource: { table: contacts, mode: single, param: id }
        layout: two-column
        action: { type: crud, operation: update, table: contacts }
        fields:
          - { field: email, label: 'Email address' }
          - { field: notes, control: textarea }

Submitting to your own endpoint

endpoint is the third submit target, beside a table and a forms[] entry: the form collects its declared fields and POSTs them as a JSON body — { [field]: value } — to any URL you name. Nothing goes through the records API, so the destination can be a platform route, an admin endpoint, or something of your own. Each field must then name its own control.

url is required and takes any path or fully-qualified URL. method is POST by default, or PUT or PATCH. responseEnvelope decides how the response body is read when judging success or failure, and is sovrium by default. submitLabel overrides the button text, onSuccess runs on a 2xx — a toast, plus the client-state effects status, refetch and reload — and onError shows a toast when the submit fails.

submitVariant is for a page that stacks several forms. One form whose submit is the page's main action wants the primary fill, and gets it by declaring nothing. A settings page drawing six one-row forms down a column gets six primary buttons instead, none of which is the main action — so each declares a quieter weight and the page regains a single focal point. The vocabulary is the button component's own: default, destructive, outline, secondary, ghost, link, fab.

app.yaml
- type: form
  endpoint:
    url: /api/account/display-name
    method: POST
    submitLabel: Save
    submitVariant: secondary
    onSuccess: { type: toast, variant: success, message: Name saved }
    onError: { type: toast, variant: destructive, message: Could not save the name }
  fields:
    - { field: name, control: text, label: Display name }

The member list is resolved through the same recipe the button component uses, so a submit and a standalone button asking for secondary cannot drift apart.

Prefilling an endpoint form

An endpoint-bound field carries its own defaultValue. Nothing derives it from a column — there is no table binding — so it is the only way such a form opens on anything but empty controls. A static value fills a text control, and on a select it is the option that arrives already chosen.

A defaultValue naming $session.<field> is the caller's own value, and it is filled in the browser rather than during rendering:

app.yaml
- type: form
  endpoint: { url: /api/invitations, method: POST, submitLabel: Send the invitation }
  fields:
    - { field: role, control: text, label: Role, defaultValue: member }
    - { field: invitedBy, control: text, label: Invited by, defaultValue: $session.name }
    - field: locale
      control: select
      label: Language
      defaultValue: fr
      options:
        - { value: en, label: English }
        - { value: fr, label: 'Français' }

Why the identity is not resolved on the server. A page is composed once and may be cached, so resolving $session.name while rendering would write whoever requested it first into every copy handed out afterwards — one reader's name arriving in the next reader's form. The served bytes therefore name nobody: the server emits the template and the browser fills it against the caller's own session. An anonymous visitor gets an empty control, never the literal $session.name, and the static defaults beside it are unaffected, since they are the same for every reader.

The resolvable fields are email, name, role and id — the same set session-bound text resolves, through the same mechanism.

It is the one type a specimen may not draw

form is excluded from the design-system catalogue. It emits a submit control unconditionally, in both its create and its update branch, and a preview frame may carry no write path — so the catalogue reports the type and its reason rather than drawing it. That is a safety rule rather than a gap in the kit.

Behaviour

Authentication Page Components

  • action type auth method login strategy email renders login form
  • Login form validates email and password before submission
  • Invalid credentials display error message
  • Successful login navigates to onSuccess navigate path and creates session in database
  • Login form requires auth strategies to include email
  • action type auth method signup strategy email renders signup form
  • Signup form validates email and password before submission
  • Successful signup creates a new user account (verified in auth.user table) and redirects
  • Password requirements are validated on signup
  • Signup form requires auth strategies to include email
  • OAuth control starts Better Auth social sign-in with a POST
  • onSuccess navigate rides the social sign-in as its callbackURL
  • OAuth login requires matching provider in auth strategies
  • visibility.when: authenticated shows component only to logged-in users
  • visibility.when: unauthenticated shows component only to guests
  • roles: [admin] restricts visibility to admin role
  • Multiple roles use OR logic for visibility
  • when and roles combine with AND logic
  • method: resetPassword renders password reset request form
  • Submitting the form sends a password reset email and creates verification record in database
  • Password reset validates token, allows setting new password, and persists change in database
  • action type auth method logout signs the session out (and gates protected pages afterward)
  • OAuth sign-in renders one painted button, never a nested control
  • User can complete full auth-components workflow (regression)
  • Logout button ends the session and gates protected pages (regression)

Create Record Form

  • action type crud with operation create renders a form
  • Form fields are generated from table schema definition
  • Required fields validated before submission
  • Successful creation triggers onSuccess redirect and persists record in database
  • Create operation requires create permission
  • create: 'all' renders the form for anonymous AND signed-in callers
  • An admin sees a create form whose role allowlist omits admin
  • A status field left untouched creates the record with its declared default
  • A relationship field left untouched is stored as NULL, not an empty string
  • A multi-select field left untouched is stored as NULL, not an empty string
  • A progress field left untouched is stored as NULL, not an empty string
  • Every constrained field type left empty is omitted from the create payload
  • SSR skeleton renders a status field as a select with its options and default
  • Hydrated form renders a status field as a combobox with its options and default
  • A barcode field is omitted when empty only if its own config constrains it
  • User can complete full create record workflow (regression)
  • User can create a record leaving every constrained field empty (regression)
  • User creates a record whose barcode fields differ only in config (regression)

Update Record Form

  • operation update renders form pre-filled with existing data
  • Form fields reflect current record values from data source
  • Modified fields validated before submission
  • Update operation requires update permission
  • update: 'authenticated' renders the form for every signed-in role, hidden for anonymous
  • Successful update persists changes to database
  • Successful update triggers onSuccess redirect
  • User can complete full update record workflow (regression)

Delete with Confirmation

  • operation delete triggers delete action
  • confirm: true displays confirmation dialog before delete
  • Delete uses soft-delete by default (verified by deleted_at timestamp in database)
  • Successful deletion triggers onSuccess handler (with database confirmation of soft-delete)
  • Delete operation requires delete permission
  • User can complete full delete workflow (regression)

Form Validation Feedback

  • Inline field errors displayed next to invalid field
  • Summary error display shows all errors at top of form
  • Server-side validation errors mapped to form fields
  • User can complete full validation workflow (regression)
  • Server-rejected values are marked on the field they belong to (regression)

CRUD with Attachment Fields

  • Create form with single-attachment field uploads file and stores reference in record
  • Create form with multiple-attachments field uploads files and stores array in record
  • Update form pre-fills attachment field with existing file (shows filename/thumbnail)
  • Update form allows replacing an existing single attachment with a new file
  • Update form allows removing an existing attachment (sets field to null)
  • Update form allows adding/removing individual files in a multiple-attachments field
  • Delete record with attachments cleans up stored files from storage backend
  • User can complete full CRUD with attachments workflow (regression)

Configurable, localizable CRUD form labels

  • Action submitLabel overrides the built-in "Create" submit-button text
  • Action fields[] override the table-derived field labels and placeholders
  • submitLabel + field labels localize through page meta.lang + app languages
  • Full configurable + localized CRUD-form label workflow

Auto-Generated Form from Table

  • type: form with dataSource.table auto-generates fields from table schema
  • Each table field type maps to appropriate input (text->input, select->dropdown, date->datepicker, richtext->editor, number->number input, boolean->checkbox, single-attachment->file upload, multiple-attachments->multi-file upload)
  • fields array includes/excludes/reorders specific fields
  • Required table fields show required indicator and validate before submit
  • dataSource.mode: single pre-fills form with existing record (edit mode)
  • action.type: crud with operation: create or update determines form behavior
  • User can complete full auto-generated form workflow (regression)

Field Configuration

  • Per-field label overrides default field name
  • Per-field placeholder shows hint text
  • Per-field readOnly: true renders non-editable display
  • Per-field defaultValue (static or $variable) pre-fills on create
  • Per-field hidden: true submits value without rendering input
  • Per-field description renders help text linked by aria-describedby, overriding the bound field's description
  • User can complete full field configuration workflow (regression)

Conditional Fields

  • visibleWhen condition shows/hides field based on another field's value
  • Condition operators: eq, neq, contains, empty, notEmpty
  • Hidden fields excluded from validation
  • Multiple conditions on same field evaluated with AND logic
  • User can complete full conditional fields workflow (regression)

Form Layout

  • layout: single-column stacks fields vertically (default)
  • layout: two-column renders fields in 2-column responsive grid
  • layout: custom allows wrapping fields in children sections
  • fieldGroups groups fields with label dividers
  • A form NESTED IN A CARD still lays out as the column it declares, and its submit is separated from the last field by a real distance — measured as computed style and geometry, never asserted by class, because an inline display from buildEmptyElementStyles beats every display utility and leaves the classes painting nothing
  • User can complete full form layout workflow (regression)

Form Actions

  • action.type: crud creates/updates record
  • action.type: automation triggers automation with form data as inputData
  • Combined: CRUD action first, then automation receives created record ID
  • Submit button props.label and props.variant customizable
  • User can complete full form actions workflow (regression)

Validation & Feedback

  • Inline field-level error messages on blur and submit
  • Summary error banner at form top listing all errors
  • Success: onSuccess toast + navigate triggered after successful submit
  • User can complete full validation workflow (regression)

File Upload Fields

  • single-attachment field type renders a file upload input (file picker button)
  • multiple-attachments field type renders a multi-file upload input (supports selecting multiple files)
  • accept prop restricts selectable file types in the browser file dialog (e.g., image/*, .pdf)
  • File size validation rejects files exceeding maxFileSize from table field definition
  • File type validation rejects files not matching allowedFileTypes from table field definition
  • dropZone: true renders a drag-and-drop area for file selection
  • Upload progress indicator shown while file is uploading
  • Image files display a thumbnail preview after selection; non-image files display filename and size
  • Selected files can be removed before form submission
  • Multi-file input enforces maxFiles limit from table field definition
  • Edit mode pre-fills file inputs with existing attachment data (filename, thumbnail) and allows replace/remove
  • File is uploaded (to storage backend) and reference stored in record on form submit
  • Validation error shown when required attachment field has no file selected
  • The upload is POSTed to the bucket declared on the bound column, and the preview URL names that same bucket
  • Edit mode renders an existing stored attachment against the column's declared bucket
  • A file chosen before the upload island mounts is kept: the filename previews and the upload POSTs exactly once
  • Text typed before a tab-panel form mounts survives and is submitted — the visible input and the saved row both carry what was entered, not what the page loaded with
  • User can complete full file upload form workflow (regression)
  • Uploads and previews honour the column's declared bucket end-to-end (regression)

File Upload with Automation

  • $form.{field} references for attachment fields resolve to file metadata object (url, name, size, mimeType)
  • $form.{field} for multiple-attachments resolves to array of file metadata objects
  • Automation inputData can reference uploaded file via $form.file and receive file metadata
  • Combined CRUD + automation: record created with attachment, then automation receives record with file URL
  • User can complete full file upload with automation workflow (regression)

Form Success Page

  • onSuccess.type: successPage replaces the form with success page content after successful submission
  • Success page with redirect navigates to the specified URL after a 2-second delay (supports $record.id variables)
  • showSummary: true displays a read-only summary of the submitted field values on the success page
  • Success page actions with action: reset resets the form to initial state for a new submission
  • Success page renders a checkmark icon, the configured title, message, and any action buttons — and the actions are painted rather than transparent, the navigate link reading as the quieter of the pair
  • User can complete full form success page workflow (regression)

Form Reset After Success

  • onSuccess.type: reset clears all fields to their default values and shows a success toast after submission
  • preserveFields array retains specified field values while clearing all others on reset
  • Multi-step wizard form resets to step 1 when onSuccess.type: reset is triggered
  • The onSuccess.type: reset schema on the in-page form component is parity-compatible with the top-level form schema — same preserveFields semantics, same wizard reset behavior
  • User can complete full form reset after success workflow (regression)

Advanced Conditional Fields

  • visibleWhen with or array shows field when ANY condition matches
  • nested and/or conditions evaluate correctly
  • requiredWhen condition makes field required only when condition is met
  • requiredWhen unmet allows form to submit without that field
  • disabledWhen condition renders field as disabled when met
  • disabled field becomes editable again when disabledWhen no longer met
  • multiple fields depending on same source field all update when source changes
  • hidden conditional field has its value excluded from submission payload
  • visibleWhen references field in previous wizard step
  • layout reflows when conditional field becomes hidden in grid

Custom-Endpoint Submit

  • An endpoint-bound form renders its explicit-control fields (typed inputs + a select) and the custom submit button
  • Submitting the form POSTs the custom endpoint with the collected field values as a JSON body (not the records API)
  • onSuccess.refetch re-reads the sibling dataSource.system grid so the new row appears without a reload
  • An endpoint-bound field honours defaultValue, so the control arrives carrying what it is about to change rather than an empty box
  • A defaultValue of $session.<field> ships as a TEMPLATE and is filled CLIENT-side, so the served bytes name nobody and a cached copy cannot carry one reader's identity to the next
  • An anonymous visitor gets an empty control, never the literal token
  • endpoint.submitVariant paints the submit in the platform button vocabulary's own words, and omitting it leaves the submit exactly as it is today
  • A select whose $session.<field> default resolves to nothing keeps its authored option instead of going blank, while one that resolves still moves to its value
  • endpoint.onSuccess.reload makes the SERVER recompose the page, and a form without it re-reads nothing
  • User can render explicit controls, POST the custom endpoint, and refetch the sibling system grid (regression, dual-dialect)

Reference Top-Level Form

  • The type: 'form' component accepts a formRef: <name> field that references app.forms[].name; an unknown name fails app schema validation with an error naming the page, the component, and the missing form
  • When formRef is set, the component inherits ALL behavior from the referenced top-level form: fields[], layout, steps[] (multi-step), conditional logic, attachment fields, onSuccess, and onError
  • formRef is mutually exclusive with each of dataSource, fields, and fieldGroups; setting any of those alongside formRef fails validation with a single error citing the conflict
  • When formRef is set, the only honored component-level overrides are display-layer props: props.label (submit button), props.variant (button variant), responsive, and visibility; component-level layout and action props are ignored with a single warning at validation time
  • The host page's access rules (e.g. pages[].access: authenticated) apply when the form is rendered through formRef; the same top-level form remains independently reachable at /forms/{name} per its own (default-public) access rules
  • Submitting the embedded form follows the top-level form's submitTo and onSuccess/onError exactly as if the user had submitted at /forms/{name} (one shared submission ledger row, one bound table write, one bound automation invocation)

Inline Multi-Step Wizard Layout

  • wizard.steps[] requires minItems: 1; an empty or missing steps[] array fails validation with a clear error naming the form component
  • Each step's fields[] references field names that exist on the inline form's fields[] (or, when fields are auto-generated, on the bound table's columns); unknown names fail validation
  • The renderer displays one step at a time with a progress indicator showing current step label and total step count; Next and Back buttons navigate between steps
  • Next-button click validates the current step's required+visible fields; failing validation blocks advancement and surfaces inline errors on the current step
  • Submit fires only on the final step; the request body includes values from all steps (skipped fields are omitted)
  • The wizard: schema accepted on the form component is identical whether the component appears under pages[].components[], top-level app.components[], or nested inside another component's children[]
  • Setting wizard: and formRef: on the same component fails validation with an error explaining that wizard layout flows from the referenced form's layout: multi-step definition instead
  • The wizard Back, Next and Submit controls are painted rather than transparent, and Back reads as the quieter of the pair
  • User can complete full inline-form wizard workflow (regression)

Last updated September 23, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium