build69
build69  /  Components  /  Forms & Flows  /  FormSection

FormSection

v1.0.4Updated 28 Sept 2026

Visual + semantic grouping of related FormField rows under a heading and optional description. Use for long forms with logical sections.

Preview

Live · 16 stories
DefaultOpen ↗
▸Why this is recommendedformsectiongroupfieldset+4

The default, out-of-the-box FormSection.

Recommended for+14
  • ✓Multi-section settings pages
  • ✓Long onboarding forms
  • ✓A long form where the reader must see at a glance which sections still have empty required fields
  • ✓Showing a section in red until it is complete and green once it is
Tags+6
formsectiongroupfieldsetrequiredcompletenessvalidationdefault
Features+4
  • Heading + description
  • Collapsible variant
  • Divider between sections
  • Two-column layout option
  • Numbered step badge
  • Progress status dot (pending / in progress / done)
  • Required-field completeness: red until every required field is answered, green once they are, with the missing fields named
  • Filled n/total readout, counting optional fields but never letting them block
  • data-complete="yes|no" for tests, and `formSectionCompletion` exported so a submit button reads the same computation
  • A section with nothing required is not painted at all
  • i18n: labels prop overrides every built-in string; the filled count formats through LocaleProvider
Not recommended for−2
  • ✕Single-field inputs (use FormField alone)

Install

Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.

get_registry_item({ name: "FormSection" })

Usage

import { FormSection } from '@/components/patterns/form-section';

<FormSection title="Billing address" description="Used on invoices.">
  <FormField label="Street"><Input /></FormField>
  <FormField label="City"><Input /></FormField>
</FormSection>

// Required-field completeness: red until both are answered, green once they are.
// The submit button reads the SAME computation, so it cannot disagree.
const fields = [
  { label: 'Street', value: street, required: true },
  { label: 'City', value: city, required: true },
  { label: 'Notes', value: notes },          // counts in n/total, never blocks
];
<FormSection title="Billing address" fields={fields} variant="card">…</FormSection>
<Button disabled={!formSectionCompletion(fields).complete}>Submit</Button>

Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.

Props

PropTypeRequired
title

Section heading

stringyes
description

Section description

string—
collapsible

Render collapsible

boolean—
step

Numbered circle badge before the title

number—
status

PROGRESS indicator — a dot beside the title. Not a validation state

'pending' | 'inProgress' | 'done'—
fields

The fields this section tracks ({ label, value, required?, filled? }). Only required ones decide the tint; all of them count in the n/total readout

FormSectionField[]—
completion

Completion passed in directly, for a section whose "filled" counts repeatable rows or signatures. Wins over `fields`

'complete' | 'incomplete'—
emphasis

How a completion state is shown. "frame" tints the whole section (the default once a state exists); "none" keeps the chip only

'none' | 'frame'—
variant

Flush block, framed card, or padding-less embed

'default' | 'card' | 'flush'—
columns

Body grid column count

1 | 2 | 3—
labels

Override every built-in string (i18n)

Partial<FormSectionLabels>—

Every tonal component shares the same tone × emphasis API, so the pair looks identical across Button, Badge, Alert and more.

Related