Text & Choice Controls
Form controls are the individual inputs a form is built from. They sit inside a form or data-form, or stand alone inside any container.
- type: form
action: { type: crud, operation: create, table: signups }
children:
- { type: input, inputType: email, props: { name: email } }
- { type: select, options: [{ label: Free, value: free }], props: { name: plan } }
- { type: switch, checked: true, props: { name: subscribe } }name is a props entry, not a schema field. No control declares name in its schema; it passes through the free-form props bag unvalidated. A typo therefore submits under the wrong key rather than failing validation. Where you can, prefer a data-form's fields array, whose field is validated against the bound table.
input
A single-line text input. The HTML input type is the schema-level inputType property — not props.type.
| Property | Description |
|---|---|
inputType |
text (default), email, password, number, tel, url, or search. |
Placeholder, default value, disabled and required all travel through props.
textarea
A multi-line text input.
| Property | Description |
|---|---|
rows |
Visible number of text lines. |
maxLength |
Maximum character count, enforced as you type. |
autoResize |
Grow the height as the content grows. |
select
A dropdown showing the selected value and opening an option list on click.
| Property | Description |
|---|---|
options |
The choices, at least one. Required unless dataSource is set below. |
dataSource |
Read the choices from a table's rows instead. See below. |
defaultValue |
Pre-selected value. |
multiple |
Allow more than one selection. |
searchable |
Enable type-ahead filtering of the options. |
searchPlaceholder |
Placeholder for the type-ahead field. |
allowCustomValue |
Accept a value that is not in options. |
An option is { label, value, disabled, icon }; label and value are required.
Options from a table
Instead of listing the choices by hand, bind the select to a table and let each row become an option:
- type: select
dataSource:
table: categories
displayField: name # required — the row field shown to the user
valueField: slug # optional — defaults to id
sort: [{ field: name, direction: asc }]
filter: [{ field: archived, operator: eq, value: false }]
limit: 50
props: { id: category-filter, label: Filter by Category }| Property | Description |
|---|---|
table |
Required. The table the option rows come from. |
displayField |
Required. The row field supplying each option's label. |
valueField |
The row field supplying each option's submitted value. Defaults to id. |
filter |
Conditions narrowing the rows, same shape as any other filter (AND logic). |
sort |
Sort rules applied in order. |
limit |
Maximum number of options. Defaults to 100, hard maximum 1000. |
options and dataSource are mutually exclusive — both answer "what are the
choices", so declaring the two together is refused at startup. displayField has
no default on purpose: guessing would silently produce a dropdown of blank rows on
any table that did not happen to match.
The rows are read on the server, before the page is sent, so the choices are in
the first response — no empty flash on arrival, and crawlers see them. That also
means the table's read permission gates the binding:
a visitor who may not read the table gets an empty dropdown, and not one of its
row values reaches the page. Filters accept $currentUser.*
references, resolved per request, so a per-user option
list is a supported binding.
Because the whole list is rendered into the page, limit caps it at 1000. A picker
over a table larger than that is a different control — one that searches the table
as you type instead of inlining it — and is not expressed here.
combobox
Now expressed as select with searchable: true, which accepts the same searchPlaceholder and allowCustomValue.
checkbox
| Property | Description |
|---|---|
checked |
Render checked. |
indeterminate |
Render the mixed-state dash — for a parent whose children are partly selected. |
radio-group
A set of mutually exclusive options.
| Property | Description |
|---|---|
options |
Required. The options to choose between. |
defaultValue |
Pre-selected option. |
orientation |
horizontal or vertical. |
switch
An on/off toggle with an accessible switch role. Accepts checked.
Use switch when the change takes effect immediately, and checkbox when it takes effect on submit — the two look similar and read very differently to someone deciding whether to press Save.
toggle / toggle-group
toggle is a single pressable button that stays pressed. toggle-group groups several.
| Property | Description |
|---|---|
pressed |
Initial pressed state (toggle only). |
options |
The grouped toggles (toggle-group only). |
toggleType |
single or multiple — how many may be active at once. |
orientation |
horizontal or vertical (toggle-group only). |
size |
Control size. |
field
A wrapper that renders a label, a control, a description and an error message as one accessible unit. It is the only form control that takes children.
| Property | Description |
|---|---|
fieldLabel |
Label associated with the child control. |
fieldDescription |
Help text below the control. |
fieldError |
Error message, rendered in the destructive tone. |
required |
Show a required indicator on the label. |
children |
The wrapped control. |
- type: field
fieldLabel: 'Email'
fieldDescription: "We'll never share it."
required: true
children:
- { type: input, inputType: email, props: { name: email } }slider
Moved to Date, Number & File Controls.
date-picker
Moved to Date, Number & File Controls.
time-picker
Moved to Date, Number & File Controls.
number-input
Moved to Date, Number & File Controls.
file-upload
Moved to Date, Number & File Controls.
Related Pages
- Date, Number & File Controls — the remaining inputs.
- Tables & Lists —
data-form, the validated way to name fields. - Forms — standalone form definitions.
- The Component Model — the
propsbag these controls use. - Field Types Overview — the table fields forms write to.
Last updated August 11, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.