Matrix fields
Matrix fields let multiple question rows share one option set. They work well for assessment scales, capability coverage, permission combinations, and bulk parameter selection. Field values are always plain JSON objects with no component state or runtime identity.
matrix-single: select one option per row.matrix-multiple: select multiple options per row.
Rows and columns are declared in the UI node's matrix property, while JSON Schema declares value types and selection limits. The compiler checks both sides and refuses to produce a FormPlan when they disagree.
Single-choice matrix matrix-single
Each row value in a single-choice matrix is a JSON primitive. Radios in the same row share a native name: Tab enters the option group, and the arrow keys move to and select an answer.
{
"assessment": {
"coordination": "expected",
"traceability": "usable",
"recovery": "expected"
}
}Controlled value shape:
Example value:
Multiple-choice matrix matrix-multiple
Each row value in a multiple-choice matrix is an array of primitives. minItems, maxItems, and uniqueItems control the minimum selection count, maximum selection count, and deduplication respectively.
{
"assessment": {
"coordination": [
"usable",
"expected"
],
"traceability": [
"expected"
],
"recovery": [
"usable"
]
}
}Once a row reaches maxItems, its unselected options are disabled while selected options can still be cleared. The limit applies only to that row and does not affect other rows.
UI node properties
Matrix fields use the shared field properties and add a strongly typed matrix configuration.
UiMatrixDefinition:
Row properties
A row id cannot be empty, contain ., or use the reserved value *. Row IDs must be unique within a matrix.
Column properties
All column values in one matrix must be string, all number, or all boolean. null is not a column value; an absent row property represents the unselected state. Column values must be unique.
Single-choice matrix Schema
The matrix itself binds to an object Schema. Each matrix.rows[].id has a matching properties entry whose enum exactly matches the column values.
The object Schema must set additionalProperties: false. Its properties cannot omit declared rows or retain orphaned properties that do not appear in matrix.rows.
Multiple-choice matrix Schema
Multiple-choice rows use array Schemas. items.enum matches the column values, and uniqueItems must be true.
minItems and maxItems cannot exceed the column count. The parent matrix Schema's required determines which rows must appear in the object. Multiple-choice rows usually also set minItems: 1 so an empty array cannot pass required validation.
Designer configuration
The component catalog provides “Single-choice matrix” and “Multiple-choice matrix” presets. A preset writes the UI node, object Schema, row Schemas, and column values together.
The properties panel supports:
- Editing row and column labels individually while showing stable row
idand columnvaluevalues. - Adding, removing, reordering, disabling, and re-enabling rows and columns.
- Updating labels by row in a collapsible bulk editor.
- Switching between single- and multiple-choice matrices while replacing row Schemas in sync.
- Preserving existing row IDs and column values so label edits do not rewrite saved form values.
The structured editor stays synchronized with the object Schema: reordering rows rebuilds properties in the same order, removing a row removes its property and required entry, and reordering columns updates every row's enum. Disabling a row or column changes only UI availability; it does not remove Schema values or existing controlled values.
New rows receive unused stable IDs and copy the current row-Schema shape. New columns keep the existing column-value type. Add buttons become disabled at the compiler limits for row count, column count, or total cell count.
The validation panel supports:
- Making the whole matrix required.
- Making individual rows required.
- Setting each row's
minItemsandmaxItemsin a multiple-choice matrix.
The design canvas shows the row and column structure directly. Interactive preview uses the real FormRenderer and returns selections as the complete controlled object.
Keyboard and screen readers
The runtime uses a semantic table, column headings, and row headings with scope="row". Each control's accessible name combines “row label: column label.” Row descriptions, selection counts, limits, and errors are associated through aria-describedby.
At container widths of 520px or less, the matrix becomes a card layout by row. Column labels appear beside each option while the DOM retains table semantics. Responsive behavior reads the embedding container's width rather than the browser viewport.
States and limits
Compiler limits:
Documents beyond these limits receive a matrix.limits diagnostic. Larger two-dimensional datasets should use paginated editing, a virtualized grid, or a dedicated host component.
Embedding boundary
Matrix values contain no submission records, authorization data, network addresses, or internal component state. An embedded host needs only a plain object connected to controlled state through value and onChange.
Dynamic columns, remote options, and cell-level business components are outside the built-in matrix contract. Implement them with reviewed custom nodes, while credentials, requests, and side effects remain under host control.
