Form Fields
A form's fields array is an ordered list of field definitions rendered top to bottom. Each field is one of five kinds, discriminated by a kind property. Table-bound fields inherit their type and validation from a table column; standalone fields are typed inline; calculation, section, and signature fields cover computed values, dividers, and signatures.
forms:
- id: 1
name: signup
title: Create your account
submitTo: { table: users }
fields:
- { kind: table-field, column: email, required: true }
- { kind: standalone, name: newsletter, inputType: checkbox, label: Subscribe to updates }
Field Kinds
| Kind | Description |
|---|---|
table-field |
Bound to a column on submitTo.table. Type, validation, and persistence flow from the table schema; the form overrides only display. |
standalone |
Typed inline via inputType; not persisted to a column directly. Use for forms that route only to automations or the ledger. |
calculation |
Read-only computed value derived from other fields via a {{fieldName}} formula. Renders no input. |
section |
Visual divider with an optional heading and description. Renders no input. |
signature |
Captures a hand-drawn or typed signature. |
Common Field Properties
Every input-bearing field kind (table-field, standalone, calculation, signature) shares a common base.
| Property | Description |
|---|---|
label |
Label shown to the submitter. Supports $t: i18n keys. |
placeholder |
Placeholder text shown when the field is empty. |
helpText |
Help text rendered below the field. |
required |
Boolean. When true, the field must have a value. For conditional requiredness use requiredWhen. |
readOnly |
Boolean. Renders the field display-only. |
hidden |
Boolean. Hides the field from the UI but still submits it (server-only value). |
defaultValue |
Literal value, or a $query.{name} / $user.{prop} reference resolved at render time. |
visibleWhen |
Conditional visibility rule. See Conditional Logic. |
requiredWhen |
Conditional required rule (same shape as visibleWhen). |
disabledWhen |
Conditional disabled rule (same shape as visibleWhen). |
permissions |
Per-field read permission. When the requesting role is excluded, the value is redacted from admin exports and per-submission detail. |
Table-Bound Fields
A kind: table-field references a column on the form's submitTo.table. The field type, validation, and persistence all come from the table schema — the form supplies only display concerns plus, for attachment columns, upload options.
| Property | Description |
|---|---|
column |
Column name on submitTo.table. Required. |
accept |
Comma-separated MIME types or extensions for attachment columns. |
maxFileSize |
Maximum size (bytes) per uploaded file (attachment columns). |
maxFiles |
Maximum number of files for multiple-attachments columns. |
dropZone |
Render a drag-and-drop zone alongside the file picker. |
fields:
- kind: table-field
column: subject
label: What can we help with?
placeholder: Briefly describe the issue
- kind: table-field
column: priority
helpText: Urgent issues are triaged first
Attachment columns (single-attachment, multiple-attachments) automatically render a file input. See File Uploads for the upload pipeline and bucket binding.
Standalone Fields
A kind: standalone field is typed inline via inputType and is not written to a table column directly. It is ideal for forms that route to an automation or live only in the submission ledger.
| Property | Description |
|---|---|
name |
Field name unique within the form (^[a-zA-Z][a-zA-Z0-9_-]*, ≤64 chars). Required. |
inputType |
Control type (see below). Required. |
options |
{ value, label } choices for select / multi-select / radio. |
accept |
Accepted MIME types for attachment input. |
maxFiles |
Max files for attachment input. |
maxFileSize |
Maximum size (bytes) per uploaded file. |
dropZone |
Render a drag-and-drop zone alongside the file picker. |
Available inputType values: short-text, long-text, email, url, phone, number, date, datetime, select, multi-select, checkbox, radio, rating, attachment.
fields:
- kind: standalone
name: rating
inputType: rating
label: How was your experience?
required: true
- kind: standalone
name: source
inputType: select
label: How did you hear about us?
options:
- { value: search, label: Search engine }
- { value: referral, label: Referral }
- { value: social, label: Social media }
Calculation, Section & Signature Fields
| Kind | Key properties |
|---|---|
calculation |
name, formula (references other fields via {{name}}), optional format (number / currency / percent / text). Read-only. |
section |
Optional heading, description, and a whole-section visibleWhen rule. Renders no input. |
signature |
name. Captures a hand-drawn or typed signature. |
fields:
- { kind: standalone, name: quantity, inputType: number, label: Quantity }
- { kind: standalone, name: unit_price, inputType: number, label: Unit price }
- kind: calculation
name: total
label: Total
formula: '{{quantity}} * {{unit_price}}'
format: currency
Prefill
The top-level prefill map seeds field values at render time. Keys are form field names; values are either a literal default or a reference:
| Reference | Resolves to |
|---|---|
$query.{name} |
A URL query-string parameter (e.g. $query.utm_source). |
$user.{prop} |
A property of the authenticated user (e.g. $user.email). Silently dropped on public forms with no session. |
$parent.{path} |
A field of the parent record — only valid for inline-relationship forms (see below). |
forms:
- id: 1
name: lead-capture
title: Request a demo
submitTo: { table: leads }
prefill:
utm_source: $query.utm_source
utm_campaign: $query.utm_campaign
fields:
- { kind: table-field, column: email, required: true }
- { kind: standalone, name: utm_source, inputType: short-text, hidden: true }
- { kind: standalone, name: utm_campaign, inputType: short-text, hidden: true }
Two ways to default a value. Per-field defaultValue accepts $query / $user references inline, while the top-level prefill map keeps prefill wiring in one place. On a public form (access.require: all), per-field defaultValue: $user.* is rejected at validation time — use top-level prefill (which tolerates an empty $user resolution) or require authentication.
Inline Relationship Create
When a form is embedded inside a parent record's detail page (e.g. a "+ New ticket" button on a Project page), the $parent.{path} prefill source ties the new child back to its parent automatically. This is configured on the page form control via inlinePrefill.
| Property | Description |
|---|---|
prefill |
Field-name → prefill-source map (same $query / $user / $parent union). |
lockPrefill |
When true, the prefilled fields are hidden and cannot be overridden (still validated server-side). Defaults to false. |
forms:
- id: 1
name: new-ticket
submitTo: { table: tickets }
fields:
- { kind: table-field, column: title, required: true }
- { kind: table-field, column: project_id }
- { kind: table-field, column: reporter_id }
pages:
- id: 1
name: project-detail
path: /portal/projects/:id
components:
- type: form
formRef: new-ticket
inlinePrefill:
prefill:
project_id: $parent.id # locked to the current project
reporter_id: $user.id
lockPrefill: true
On submit, the engine revalidates parent existence to defend against stale $parent references. Both single and multi-relationship columns are supported.
Related Pages
- Forms Overview — form metadata, submit targets, access control.
- Conditional Logic —
visibleWhen/requiredWhen/disabledWhen. - Multi-Step Forms — distributing fields across steps.
- File Uploads — attachment input options and bucket binding.
- Relational Fields — the relationship columns
$parenttargets. - Form Controls — the page component that hosts
formRef+inlinePrefill.