<- Back to Forms Components

Form renders a complete form from a schema in model. The component does not receive TextField, Select, Attachment, or other input components as children: the renderer creates those controls from the field definitions.

Use Form when a module needs a structured submit payload, client-side validation, field-level errors, loading state, and a predictable form layout.

Contract

Property Type Notes
model list[dict] Field and layout schema.
values dict or binding Initial values keyed by field name. Supports static values, store binding, and data-model binding.
submit_label str Submit button label. Defaults to Submit.
action str or action object Action called after successful client-side validation. Action objects can include confirm.
params dict Extra context merged into the submit action payload.
track_loading str or list[str] Action names used to show the submit loading state.
errors dict[str, str] Server-side field errors keyed by field name.

values is used to initialize the fields. Editing the form does not automatically write the changed values back to store or data model. The updated values are sent to the submit action as one object under the form component id.

Module actions used by action and track_loading must be fully qualified, for example components.forms_form_submit. The Python decorator keeps the local action name, while the component dispatch uses module.action. See Action Dispatch.

Action Confirmation

Use an action object with confirm when the submit action must be confirmed after client-side validation and before backend dispatch. If validation fails, no confirmation dialog is shown. If the user cancels the dialog, no action is sent.

The component test page exposes a form submit counter and the last received form payload so the dispatch can be checked directly after confirming.

form = sdk.ui.Form(
    "confirm_python_form",
    model=[
        {
            "type": "text",
            "name": "title",
            "label": sdk.i18n.t("components.form.confirm.title_label"),
            "value": "Python title",
            "validations": [
                {
                    "rule": "required",
                    "message": sdk.i18n.t("components.form.confirm.title_required"),
                }
            ],
        },
        {
            "type": "textarea",
            "name": "notes",
            "label": sdk.i18n.t("components.form.confirm.notes_label"),
            "value": "Python notes",
        },
    ],
    submit_label=sdk.i18n.t("components.form.confirm.python_submit"),
)
form.set_prop(
    "action",
    {
        "name": "components.form_confirm_action",
        "context": {"source": "python_form"},
        "confirm": {
            "text": sdk.i18n.t("components.form.confirm.prompt"),
            "confirm_text": sdk.i18n.t("components.form.confirm.accept"),
            "cancel_text": sdk.i18n.t("components.form.confirm.cancel"),
        },
    },
)

Model Structure

model is a list of nodes. Each node is either a layout node or a field node.

Layout Nodes

Key Type Notes
type row or column Defines the layout direction.
children list[dict] Child nodes.
items list[dict] Alternative name for children.
spacing int Gap between children.
span int Proportional size when the node is inside a row.
stretch int Alternative proportional size.

Rows distribute children horizontally on desktop-sized surfaces and vertically on narrow web viewports. Use span or stretch on row children to control their relative width.

Field Nodes

Key Type Notes
type str Field renderer type. Unknown types fall back to text input.
name str Required. This is the key used in values, errors, and submit payload.
label str Field label.
placeholder str Placeholder for text-like inputs and select.
value any Field-level initial value. Usually supplied through values instead.
span int Proportional width inside a row.
stretch int Alternative proportional width inside a row.
validations list[dict] Client-side validation rules.
show_if rule Shows the field only when the rule evaluates to true.
hide_if rule Hides the field when the rule evaluates to true.

Field Visibility

Field nodes support show_if and hide_if inside model. These rules use the same grouped condition shape as component visibility, with one extra form-local binding: $form.<field_name>.

When a field is hidden, the form does not render it, does not validate it, does not materialize its attachments, and does not include it in the submit payload.

- kind: Form
  id: reasoning_form
  action: system.save_reasoning_options
  model:
    - type: select
      name: reasoning_activation_type
      label: Activation type
      value: boolean
      options:
        - label: Boolean
          value: boolean
        - label: Select
          value: select
    - type: checkbox
      name: reasoning_enabled
      label: Enable reasoning
      show_if:
        conditions:
          - left: "$form.reasoning_activation_type"
            op: "=="
            right: boolean
    - type: select
      name: reasoning_effort
      label: Reasoning effort
      options:
        - label: Low
          value: low
        - label: Medium
          value: medium
      show_if:
        conditions:
          - left: "$form.reasoning_activation_type"
            op: "=="
            right: select

Use show_if for the positive case and hide_if for direct negative guards. If both are present, the field must pass show_if and must not match hide_if.

Field Types

Type Rendered input Main options Collected value
text TextField placeholder, validations str
email TextField placeholder, validations str
password TextField in password mode placeholder, validations str
textarea TextArea placeholder, rows, auto_resize, validations str
checkbox Checkbox validations bool
toggle Toggle validations bool
select Select options, multiple, placeholder, validations str or list[str]
tags, tags_input TagsInput item_schema, placeholder, validations list
editable_list EditableList item_schema, add_label, remove_label, submit_label, validations list[str]
radio, radio_group RadioGroup options, validations str
date DatePicker min_date, max_date, format, validations formatted str
datetime, date_time DatePicker with date-time format min_date, max_date, format, validations formatted str
attachment, file Attachment accept, multiple, validations list[dict]

Combobox and FolderSelector are standalone form inputs. They are not part of the documented Form.model field set.

tags uses the TagsInput item schema. editable_list uses the EditableList item schema. Both field types are list-valued and can be validated with required.

Schema Reference

The root model value is always a list. Each item must be a dict-like node.

row

row groups children on the same horizontal line when the viewport has enough width.

Key Required Type Notes
type yes "row" Layout node type.
children no list[dict] Child layout or field nodes.
items no list[dict] Alternative to children.
spacing no int Gap between children.
span no int Proportional size when this row is itself inside another row.
stretch no int Alternative to span.

Each child inside a row can define span or stretch. If omitted, the child uses 1.

column

column groups children vertically.

Key Required Type Notes
type yes "column" Layout node type.
children no list[dict] Child layout or field nodes.
items no list[dict] Alternative to children.
spacing no int Gap between children.
span no int Proportional size when this column is inside a row.
stretch no int Alternative to span.

Common Field Keys

These keys are accepted by every documented field type.

Key Required Type Notes
type yes str Field type.
name yes str Key used inside values, errors, and the submitted form object.
label no str Visible field label. Defaults to name in renderers that need a fallback.
placeholder no str Used by text-like fields and select.
value no any Field-level initial value. Root values usually supplies this instead.
span no int Proportional width inside a row.
stretch no int Alternative to span.
validations no list[dict] Client-side validation rules.
show_if no rule Shows the field only when the rule evaluates to true. Supports $form.<field_name>.
hide_if no rule Hides the field when the rule evaluates to true. Supports $form.<field_name>.

text

Key Required Type Notes
type yes "text" Text input.
name yes str Key in the submitted form object.
label no str Field label.
placeholder no str Empty-state hint.
value no str Initial value.
validations no list[dict] Supports required, regex, min_length, max_length, equals_field, same_as.

email

Same schema as text, with type: "email". The component does not add a built-in email rule; define regex when the format must be enforced.

password

Same schema as text, with type: "password". The input is rendered in password mode.

textarea

Key Required Type Notes
type yes "textarea" Multiline text input.
name yes str Key in the submitted form object.
label no str Field label.
placeholder no str Empty-state hint.
value no str Initial value.
rows no int Initial row count. Defaults to 3 in the renderer.
auto_resize no bool TextArea auto-resize flag.
validations no list[dict] Supports text validation rules.

checkbox

Key Required Type Notes
type yes "checkbox" Boolean checkbox.
name yes str Key in the submitted form object.
label no str Field label.
value no bool Initial checked state.
validations no list[dict] required requires the value to be true.

toggle

Same schema as checkbox, with type: "toggle".

select

Key Required Type Notes
type yes "select" Select input.
name yes str Key in the submitted form object.
label no str Field label.
placeholder no str Empty-state hint.
value no str or list[str] Initial selected value. Use a list when multiple is true.
options no list[dict] Each option has label and value.
multiple no bool Enables multi-select and list values.
validations no list[dict] required rejects empty selection.

radio / radio_group

Key Required Type Notes
type yes "radio" or "radio_group" RadioGroup input.
name yes str Key in the submitted form object.
label no str Field label.
value no str Initial selected value.
options no list[dict] Each option has label and value.
validations no list[dict] Supports required and string comparison rules.

date

Key Required Type Notes
type yes "date" DatePicker input.
name yes str Key in the submitted form object.
label no str Field label.
value no str Initial formatted date.
format no str Defaults to yyyy-MM-dd.
min_date no str Minimum selectable date.
max_date no str Maximum selectable date.
validations no list[dict] Supports common validation rules.

datetime / date_time

Same schema as date, with type: "datetime" or type: "date_time". The default format is yyyy-MM-dd HH:mm.

attachment / file

Key Required Type Notes
type yes "attachment" or "file" Attachment input.
name yes str Key in the submitted form object.
label no str Field label.
value no list[dict] Initial attachment entries.
accept no str Accepted extensions or MIME types.
multiple no bool Allows more than one attachment.
ingest no bool Defaults to true. When false, uploaded files are not enqueued for knowledge extraction.
validations no list[dict] required rejects an empty list.

Option Shapes

select and radio options use this shape:

{"label": "Admin", "value": "admin"}

For select with multiple: true, the value must be a list.

{"type": "select", "name": "interests", "multiple": True, "options": interest_options}

For attachment and file, accept contains the accepted extensions or MIME types and multiple controls whether the value can contain more than one entry.

{"type": "attachment", "name": "attachments", "accept": ".pdf,.txt", "multiple": True}

Validation

Field definitions can include validations.

Rule Required keys Meaning
required message Value must be present. For checkbox/toggle it must be true.
regex pattern, message String value must match the regular expression. Empty values are handled by required.
min_length value, message String or list must have at least value chars/items.
max_length value, message String or list must have at most value chars/items.
equals_field, same_as field or value, message Value must equal another field value.

Client-side validation runs before submit. If validation fails, the submit action is not called.

Values

values can be a static dict, a store binding, or a data-model binding.

form_values = {
    "first_name": "Ada",
    "last_name": "Lovelace",
    "email": "ada@example.com",
    "role": "admin",
    "interests": ["ai", "docs"],
    "terms": True,
}

static_form = sdk.ui.Form(
    "components_test_form_static",
    model=form_model,
    values=form_values,
    submit_label="Submit static-values form",
    action="components.forms_form_submit",
    params={"output_key": "output_static"},
)

store_form = sdk.ui.Form(
    "components_test_form_store",
    model=form_model,
    values=bound.store("/components_test/form/values", scope="page", default=form_values),
    submit_label="Submit store-bound form",
    action="components.forms_form_submit",
    params={"output_key": "output_store"},
)

data_form = sdk.ui.Form(
    "components_test_form_data",
    model=form_model,
    values=bound.data("/components_test/form_model/values", default=form_values),
    submit_label="Submit data-model-bound form",
    action="components.forms_form_submit",
    params={"output_key": "output_data"},
)

Complete Model Example

form_model = [
    {
        "type": "row",
        "spacing": 16,
        "children": [
            {
                "type": "text",
                "name": "first_name",
                "label": "First name",
                "placeholder": "Ada",
                "span": 1,
                "validations": [{"rule": "required", "message": "First name is required."}],
            },
            {
                "type": "text",
                "name": "last_name",
                "label": "Last name",
                "placeholder": "Lovelace",
                "span": 1,
                "validations": [{"rule": "required", "message": "Last name is required."}],
            },
        ],
    },
    {
        "type": "row",
        "spacing": 16,
        "children": [
            {
                "type": "email",
                "name": "email",
                "label": "Email",
                "span": 2,
                "validations": [
                    {"rule": "required", "message": "Email is required."},
                    {"rule": "regex", "pattern": r"^[^@\s]+@[^@\s]+\.[^@\s]+$", "message": "Use a valid email address."},
                ],
            },
            {
                "type": "password",
                "name": "password",
                "label": "Password",
                "span": 1,
                "validations": [{"rule": "min_length", "value": 6, "message": "Use at least 6 characters."}],
            },
        ],
    },
    {
        "type": "textarea",
        "name": "bio",
        "label": "Bio",
        "rows": 3,
        "validations": [{"rule": "max_length", "value": 180, "message": "Keep the bio under 180 characters."}],
    },
    {
        "type": "row",
        "spacing": 16,
        "children": [
            {
                "type": "select",
                "name": "role",
                "label": "Role",
                "options": [
                    {"label": "User", "value": "user"},
                    {"label": "Editor", "value": "editor"},
                    {"label": "Admin", "value": "admin"},
                ],
            },
            {
                "type": "select",
                "name": "interests",
                "label": "Interests",
                "multiple": True,
                "options": [
                    {"label": "AI", "value": "ai"},
                    {"label": "Docs", "value": "docs"},
                    {"label": "Automation", "value": "automation"},
                ],
            },
            {
                "type": "radio",
                "name": "plan",
                "label": "Plan",
                "options": [
                    {"label": "Free", "value": "free"},
                    {"label": "Pro", "value": "pro"},
                    {"label": "Enterprise", "value": "enterprise"},
                ],
            },
        ],
    },
    {
        "type": "row",
        "spacing": 16,
        "children": [
            {"type": "date", "name": "start_date", "label": "Start date", "min_date": "2026-01-01", "max_date": "2026-12-31"},
            {"type": "datetime", "name": "meeting_at", "label": "Meeting", "format": "yyyy-MM-dd HH:mm"},
        ],
    },
    {"type": "attachment", "name": "attachments", "label": "Attachments", "accept": ".pdf,.txt", "multiple": True, "ingest": True},
    {
        "type": "row",
        "spacing": 16,
        "children": [
            {"type": "checkbox", "name": "terms", "label": "Accept terms", "validations": [{"rule": "required", "message": "Terms must be accepted."}]},
            {"type": "toggle", "name": "newsletter", "label": "Newsletter"},
        ],
    },
]

Submit Payload

The submit action receives:

  • form_id
  • the full values dict under ctx[form_id]
  • every key from params
@action("forms_form_submit")
async def forms_form_submit(ctx: dict, session: dict, sdk) -> dict:
    form_id = ctx.get("form_id")
    values = ctx.get(form_id, {})
    payload = {
        "form_id": form_id,
        "form_values": values,
        "output_key": ctx.get("output_key"),
    }
    return sdk.effects.respond(
        sdk.effects.ui_messages([
            {
                "stateUpdate": {
                    "scope": "page",
                    "values": {
                        "/components_test/form/output_static": json.dumps(payload),
                    },
                }
            }
        ])
    )

Visibility And Permissions

Form supports the shared show_if, hide_if, and required_permissions contract.

form.set_show_if({
    "conditions": [
        {"left": bound.store("/components_test/form/show", scope="page", default=True), "op": "==", "right": True}
    ]
})
form.set_hide_if({
    "conditions": [
        {"left": bound.store("/components_test/form/hide", scope="page", default=False), "op": "==", "right": True}
    ]
})
form.set_required_permissions(["components.forms.submit"])

Practical Rules

  • Use model for field structure; do not pass input components as children.
  • Use values to prefill create/edit forms from static data, page/global store, or the surface data model.
  • Use row and column nodes as the first-level layout containers when the form needs predictable field distribution.
  • Use validations for client-side blocking validation.
  • Use errors for server-side field errors keyed by field name.
  • Use the submit action for persistence; Form does not automatically persist edited values to store or data model.