
# Form Field Groups

A twenty-field form rendered as one flat column asks the submitter to read every input before understanding any of them. The same fields under three headings — "About you", "Your experience", "Why us" — become something a person can scan and answer a section at a time.

`fieldGroups` declares those headings. Each entry is a label plus the field names that belong under it:

```yaml
forms:
  - id: 1
    name: apply
    title: Apply for Senior Engineer
    submitTo: { table: candidates }
    fieldGroups:
      - label: About you
        fields: [first_name, last_name, email]
      - label: Your experience
        fields: [years_of_experience, github_url]
    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: standalone, name: years_of_experience, inputType: number }
      - { kind: standalone, name: github_url, inputType: url }
```

## Group Properties

| Property      | Description                                                                                    | Default         |
| ------------- | ---------------------------------------------------------------------------------------------- | --------------- |
| `label`       | Section heading rendered above the group's fields. Must be at least one character.             | Required.       |
| `fields`      | Field names belonging to this section, in the order they should render. At least one required. | Required.       |
| `visibleWhen` | Condition gating the whole section. Same shape and operators as a field's `visibleWhen`.       | Always visible. |

`fieldGroups` is itself optional. Omit it and the form renders `fields[]` top to bottom with no headings — which is the right choice for a form short enough not to need signposting. When present, the array must declare at least one group.

## Order Comes From the Groups

Once `fieldGroups` exists, it drives layout: sections render in array order, and each field renders in the position its group gives it. That means grouping can reorder a form without touching `fields[]` — the `fields[]` array keeps defining _what_ the fields are, the groups define _where_ they appear.

A field you never list still renders. It falls through to the bottom, after every section, in `fields[]` source order. Grouping half a form is therefore valid: the named fields get headings, the rest trail behind as an unlabeled block.

## Conditional Sections

`visibleWhen` gates an entire section — label and every field under it — on the value of another field, using the same condition primitive as field-level rules:

```yaml
fieldGroups:
  - label: Preferences
    fields: [wants_relocation]
  - label: Relocation
    fields: [relocation_country, relocation_date]
    visibleWhen: { field: wants_relocation, operator: eq, value: true }
```

The rule is evaluated server-side. On first render there are no answers yet, so a conditional section starts hidden and appears once its trigger is answered. On submit the rule is evaluated again, this time against the submitted values, and a section whose rule is false is treated as if it were never part of the form: its required fields do not block the submission.

:::callout
**A hidden section drops its values, it does not merely skip validation.** When a group's `visibleWhen` is false at submit time, every field listed under it is stripped from the payload before the table write and the ledger write. A value the submitter typed while the section was open — then hid again by changing the trigger — is not persisted. Never put a field you always need inside a conditional group.
:::

## Groups Versus Steps

Both carve a form into parts, and both take a `visibleWhen`. The difference is what the submitter sees at once.

| Use           | When                                                                                                                                |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `fieldGroups` | All fields stay on one page and one scroll; headings give structure. Requires `layout: single-page` (the default).                  |
| `steps`       | Fields are split across screens with prev/next navigation and per-step validation. Requires `layout: multi-step` or `one-question`. |

## Related Pages

- [Form Fields](/en/docs/form-fields) — the `fields[]` entries a group refers to by name.
- [Conditional Logic](/en/docs/form-conditional-logic) — the operator vocabulary `visibleWhen` draws on.
- [Multi-Step Forms](/en/docs/form-multi-step) — `steps[]`, the multi-screen alternative to groups.
- [Forms Overview](/en/docs/forms-overview) — where `fieldGroups` sits in the full form schema.
