
# Multi-Step Forms

Long forms are easier to complete when they are broken into manageable pieces. The `layout` property chooses how a form's fields are presented: all at once, split into steps with previous/next navigation, or one field per screen Typeform-style. Steps support conditional skipping and branching so the path through the form can adapt to the submitter's answers.

```yaml
forms:
  - id: 1
    name: onboarding
    title: Set up your workspace
    layout: multi-step
    submitTo: { table: workspaces }
    fields:
      - { kind: standalone, name: workspace_name, inputType: short-text, required: true }
      - { kind: standalone, name: team_size, inputType: number }
      - { kind: standalone, name: use_case, inputType: long-text }
    steps:
      - { id: basics, title: The basics, fields: [workspace_name] }
      - { id: team, title: Your team, fields: [team_size] }
      - { id: goals, title: Your goals, fields: [use_case] }
```

## Layout Modes

| Mode           | Description                                                                        |
| -------------- | ---------------------------------------------------------------------------------- |
| `single-page`  | All fields on one page (default). Steps are ignored.                               |
| `multi-step`   | Fields grouped into `steps[]` with previous/next navigation.                       |
| `one-question` | One field per screen, advancing automatically. Driven by the same `steps[]` model. |

## Step Definitions

When `layout` is `multi-step` or `one-question`, the `steps` array partitions the form's fields into ordered screens. Each step references field names defined in the parent `fields` array.

| Property      | Description                                                                                                                                      |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`          | Unique step id within the form (kebab-case recommended, ≤64 chars). Required.                                                                    |
| `title`       | Step title shown above the fields.                                                                                                               |
| `description` | Step intro paragraph.                                                                                                                            |
| `fields`      | Field names belonging to this step. Each must match a field's `name` (standalone/calculation/signature) or `column` (table-field). At least one. |
| `visibleWhen` | Whole-step visibility rule. When false, the entire step is skipped. Uses the [conditional](/en/docs/form-conditional-logic) shape.               |
| `goToWhen`    | Branching rules. The first matching rule jumps to its `goTo` step instead of the linear next step.                                               |

## Navigation & Branching

By default a multi-step form advances linearly: step 1 → step 2 → step 3. Two mechanisms override that flow.

**Skip a step** with a step-level `visibleWhen` — when the condition is false the step is removed from the sequence entirely.

**Branch to a different step** with `goToWhen`. Each rule has a `when` condition and a `goTo` target step id; the first matching rule wins, otherwise linear flow continues.

```yaml
steps:
  - id: plan
    title: Choose a plan
    fields: [plan]
    goToWhen:
      # Enterprise plans skip self-serve billing and route to a sales step
      - when: { field: plan, operator: eq, value: enterprise }
        goTo: sales
  - id: billing
    title: Payment
    fields: [card_number]
  - id: sales
    title: Talk to sales
    fields: [company, seats]
    # only relevant for enterprise; hidden otherwise
    visibleWhen: { field: plan, operator: eq, value: enterprise }
```

:::callout
**`goTo` targets must exist.** Each `goToWhen[].goTo` must match a `steps[].id` in the same form. A linear next step is taken whenever no `goToWhen` rule matches.
:::

## One-Question-at-a-Time

The `one-question` layout presents a single field per screen for a focused, conversational experience. It reuses the same `steps[]` model — typically one field per step — and the same `visibleWhen` / `goToWhen` branching applies.

```yaml
forms:
  - id: 2
    name: survey
    title: Quick survey
    layout: one-question
    submitTo: { storeSubmission: true }
    display:
      progressBar: true
    fields:
      - {
          kind: standalone,
          name: nps,
          inputType: rating,
          label: How likely are you to recommend us?,
        }
      - {
          kind: standalone,
          name: reason,
          inputType: long-text,
          label: What's the main reason for your score?,
        }
    steps:
      - { id: q1, fields: [nps] }
      - { id: q2, fields: [reason] }
```

## Display Overrides

The `display` object adjusts how the form looks without affecting submission semantics.

| Property      | Description                                                                                      |
| ------------- | ------------------------------------------------------------------------------------------------ |
| `columns`     | Number of columns the fields are arranged in (1–4). Applies to single-page and per-step layouts. |
| `progressBar` | Show a progress bar in `multi-step` / `one-question` layouts. No effect on `single-page`.        |
| `submitLabel` | Submit button label (`Submit` by default). Supports `$t:` keys.                                  |
| `theme`       | Per-form overrides: `primaryColor`, `backgroundColor`, `borderRadius`.                           |

```yaml
forms:
  - id: 3
    name: registration
    title: Event registration
    layout: multi-step
    display:
      columns: 2
      progressBar: true
      submitLabel: Complete registration
      theme:
        primaryColor: '#4f46e5'
        borderRadius: 0.5rem
    submitTo: { table: registrations }
    fields:
      - { kind: table-field, column: first_name, required: true }
      - { kind: table-field, column: last_name, required: true }
      - { kind: table-field, column: email, required: true }
      - { kind: table-field, column: dietary_notes }
    steps:
      - { id: name, title: Your name, fields: [first_name, last_name] }
      - { id: contact, title: Contact & preferences, fields: [email, dietary_notes] }
```

## Related Pages

- [Form Fields](/en/docs/form-fields) — the fields distributed across steps.
- [Conditional Logic](/en/docs/form-conditional-logic) — the condition shape used by `visibleWhen` and `goToWhen`.
- [Forms Overview](/en/docs/forms-overview) — `layout`, `steps`, and `display` in the full form schema.
- [Submissions](/en/docs/form-submissions) — what happens after the final step.
