Forms
Forms should feel consistent regardless of who built them. These are the decisions that come up most often.
Layout
The default is a single column, labels above their inputs, following the Form component's header / content / footer structure. Cap content width at 480–560 px so no line runs too long.
- Left rail (type selector) — use one only when the form has mutually exclusive variants that change which fields appear: chart type (Line / Number), monitor type (Cron / Heartbeat). Without variants, no rail.
- Right side panel — reserve it for one thing: a preview. Conceptual explanations ("What is a heartbeat?") do not go here; move them to a one-line header description with a Learn more link.
Side panel = preview. Educational and reference content goes in the header description and a link.
Required markers
Mark required fields with Required to the right of the label, the same way in every form. Don't mark optional fields — no marker means optional.
Today only Storage and Monitor show it and Chart leaves it out, so the rule reads differently from form to form. That inconsistency is the thing to fix, not a precedent to follow.
Choosing the control
Use the same control for the same job. Let the decision the reader is making drive the choice — how many options, and when the change takes effect.
| Control | Use when | Example in product |
|---|---|---|
| Radio group | One choice from 2–5 mutually exclusive options, all worth showing at once | Draw NULL as 0 / Draw NULL as NULL |
| Select (single) | One choice from a longer or predefined list, or when the value drives other content on the page | Graph display, Data format |
| Select (multiple) | Several values from a predefined list, stored as one field | Environments, tags |
| Combobox | A select with a search field, for a long or data-driven list | Apps, members, namespaces |
| Checkbox (multiple) | Several independent on/off options | Notify me through: Email, Slack |
| Checkbox (single) | A single on/off setting submitted with the form | Compress exports, Start Y-axis at 0 |
| Toggle | An on/off setting that applies instantly, no submit | Enable a feature in settings |
| Menu | Actions or navigation — not form data | Row actions (⋯), contextual menus |
The short version: mutually exclusive → radio (few) or select (many). Long or data-driven list → combobox. Independent on/off → checkbox. Instant on/off → toggle. Actions → menu.
- Radio vs select — five or fewer options and seeing them all helps? Radio. Longer lists, or when the choice changes other content (filters a table, switches a view, updates dependent fields)? Select.
- Checkbox vs toggle — inside a form saved on submit, use a checkbox. Reserve the toggle for settings that take effect the moment they're flipped.
- Never collect form data with a menu. A menu runs commands and stores nothing. To store a chosen value, use a select. Both open the same dropdown, so the trigger and the behaviour are what tell them apart. See menu.
- Give every radio and checkbox group a group label, and keep option labels short and parallel.
Field help
Before adding supporting text, ask whether someone can complete the field correctly without it. Then pick the weakest mechanism that works.
- Can't be filled correctly without it (format, constraints, result) → an explanation, below the label or input, always visible. "Lowercase, numbers,
_and-only". "Stored encrypted." This is the default. - Nice to know, field works without it → a tooltip on a single
(i)icon next to the label. Never hide required guidance here. Standardise on(i); drop(?). - Needs context or a warning at form or group level → the header description (1–2 sentences) or a Message: beta notices, destructive-action warnings. Trim long explanations to one line plus Learn more.
- Needs an example of the value → a placeholder, prefixed
e.g.so it isn't mistaken for a real value:e.g. background_job. Never a placeholder in place of a label or required guidance.
Previews
Add a preview panel only when both are true:
- the result is visual — charts, dashboards, notification templates; and
- the relationship between input and result isn't obvious at a glance.
Don't add one where the result is text or config (storage credentials, a monitor identifier). Keep the preview sticky, with any controls it needs such as a time range. When it has nothing to show, use the empty state pattern.
Dividers and grouping
Group the fields by meaning first, then decide how to separate the groups.
- By shared purpose — fields doing the same job (connection details, options, notifications) go together.
- In task order — follow the order people fill things in. Required and frequently used groups first.
- A section label per group — short: Connection, Options.
| Situation | How to separate |
|---|---|
| 4 fields or fewer, one group | Spacing only. No divider, no label |
| 5+ fields and 2+ distinct groups | Divider + section label |
| The group's purpose changes | Divider + section label |
| A group has more than 6 fields | Consider splitting it |
⚠️ Don't over-segment. A divider signals "the purpose changes here". With one group, leave it out.
Input sizing
Input width predicts how long the value should be. Don't put a short value — a number plus a unit — in a full-width input. Narrow it to match, and keep the unit beside the input.