1
ComponentsForm Controls

Fieldset

Fieldset groups related form fields under a shared semantic legend.

Purpose

Fieldset groups related form fields under one shared semantic legend. A standalone field does not need a Fieldset.

Grouped fields

Profile
import { Fieldset, FieldsetLegend } from "@/components/ui/fieldset";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function Example() {
  return (
    <Fieldset>
      <FieldsetLegend>Profile</FieldsetLegend>
      <Field><FieldLabel>Name</FieldLabel><Input /></Field>
    </Fieldset>
  );
}

Installation

With the Siteplane registry alias configured, add the component and import the installed source from your app.

CLI

pnpm dlx shadcn@4.16.1 add @siteplane/fieldset

Import

import { Fieldset, FieldsetLegend } from "@/components/ui/fieldset";

Need the machine-readable source metadata? View registry JSON.

Usage

Use Fieldset to group related fields under one legend when several fields form one decision or setting.

import { Fieldset, FieldsetLegend } from "@/components/ui/fieldset";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function Example() {
  return (
    <Fieldset>
      <FieldsetLegend>Profile</FieldsetLegend>
      <Field><FieldLabel>Name</FieldLabel><Input /></Field>
    </Fieldset>
  );
}

Examples

Disabled state, layout groupings, choice sets and form integration are compositions of the same primitive.

Disabled

The native disabled attribute deactivates and dims every child control.

Contact (disabled)
<Fieldset disabled>
  <FieldsetLegend>Contact (disabled)</FieldsetLegend>
  <Field>
    <FieldLabel htmlFor={emailId}>Email</FieldLabel>
    <Input id={emailId} placeholder="ada@example.com" type="email" />
  </Field>
  <Field>
    <FieldLabel htmlFor={phoneId}>Phone</FieldLabel>
    <Input id={phoneId} placeholder="+43 ..." type="tel" />
  </Field>
</Fieldset>

Two column

Billing details
<Fieldset className="grid gap-3">
  <FieldsetLegend>Billing details</FieldsetLegend>
  <div className="grid gap-3 sm:grid-cols-2">
    <Field>
      <FieldLabel htmlFor={firstId}>First name</FieldLabel>
      <Input id={firstId} placeholder="Ada" />
    </Field>
    <Field>
      <FieldLabel htmlFor={lastId}>Last name</FieldLabel>
      <Input id={lastId} placeholder="Lovelace" />
    </Field>
    <Field>
      <FieldLabel htmlFor={cityId}>City</FieldLabel>
      <Input id={cityId} placeholder="Vienna" />
    </Field>
    <Field>
      <FieldLabel htmlFor={zipId}>ZIP</FieldLabel>
      <Input id={zipId} placeholder="1010" />
    </Field>
  </div>
</Fieldset>

Nested fieldsets

Phone numbers
Home
Work
<Fieldset className="grid gap-3">
  <FieldsetLegend>Phone numbers</FieldsetLegend>
  <Fieldset className="grid gap-2 rounded-lg border p-3">
    <FieldsetLegend className="text-sm">Home</FieldsetLegend>
    <Field>
      <FieldLabel htmlFor={homeId}>Number</FieldLabel>
      <Input id={homeId} placeholder="+43 ..." type="tel" />
    </Field>
  </Fieldset>
  <Fieldset className="grid gap-2 rounded-lg border p-3">
    <FieldsetLegend className="text-sm">Work</FieldsetLegend>
    <Field>
      <FieldLabel htmlFor={workId}>Number</FieldLabel>
      <Input id={workId} placeholder="+43 ..." type="tel" />
    </Field>
  </Fieldset>
</Fieldset>

Legend with description

Workspace

This name is visible to every member.

<Fieldset aria-describedby={descriptionId} className="grid gap-3">
  <div className="grid gap-1">
    <FieldsetLegend>Workspace</FieldsetLegend>
    <p className="text-muted-foreground text-xs" id={descriptionId}>
      This name is visible to every member.
    </p>
  </div>
  <Field>
    <FieldLabel htmlFor={nameId}>Workspace name</FieldLabel>
    <Input id={nameId} placeholder="plane" />
  </Field>
</Fieldset>

Choice set

Notifications

Channels

<Fieldset className="grid gap-4">
  <FieldsetLegend>Notifications</FieldsetLegend>
  <Field>
    <FieldDescription>Channels</FieldDescription>
    <Label className="cursor-pointer text-sm">
      <Checkbox defaultChecked value="email" />
      Email
    </Label>
    <Label className="cursor-pointer text-sm">
      <Checkbox value="sms" />
      SMS
    </Label>
  </Field>
  <Field>
    <FieldLabel id={planLabelId}>Frequency</FieldLabel>
    <RadioGroup aria-labelledby={planLabelId} defaultValue="daily">
      <Label className="cursor-pointer text-sm">
        <Radio value="daily" />
        Daily digest
      </Label>
      <Label className="cursor-pointer text-sm">
        <Radio value="weekly" />
        Weekly summary
      </Label>
    </RadioGroup>
  </Field>
</Fieldset>

Settings section

Privacy

Control what others can see about you.

<Fieldset aria-describedby={descriptionId} className="grid gap-3">
  <div className="grid gap-1">
    <FieldsetLegend>Privacy</FieldsetLegend>
    <p className="text-muted-foreground text-xs" id={descriptionId}>
      Control what others can see about you.
    </p>
  </div>
  <Label className="flex cursor-pointer items-center justify-between gap-6 text-sm">
    <span>Show online status</span>
    <Checkbox defaultChecked value="online" />
  </Label>
  <Label className="flex cursor-pointer items-center justify-between gap-6 text-sm">
    <span>Allow profile indexing</span>
    <Checkbox value="indexing" />
  </Label>
</Fieldset>

Fieldset in form

Profile
Profile: not submitted
<Form onSubmit={onSubmit}>
  <Fieldset className="grid gap-3">
    <FieldsetLegend>Profile</FieldsetLegend>
    <Field name="displayName">
      <FieldLabel>Display name</FieldLabel>
      <Input placeholder="ada" required />
      <FieldError>Please enter a display name.</FieldError>
    </Field>
  </Fieldset>
  <Button loading={loading} type="submit">Save</Button>
</Form>

API Reference

The reference lists the source-owned exports and props that are easy to miss in visual examples. Inherited Base UI props remain available unless the wrapper narrows them.

  • Fieldset renders a native fieldset and FieldsetLegend provides its group name.
  • Put FieldsetLegend first, then compose each control with Field.
  • disabled disables all descendant form controls.
  • The default layout is a grid with system spacing; use existing spacing classes only when the composition needs a different density.

Motion

Component motion uses the shared Siteplane motion contract. See the global motion guide for provider setup, tokens and reduced-motion behavior.

  • Fieldset has no animation.
  • Descendant controls retain their own focus, disabled, and reduced-motion behavior.
  • Disabled and nested Fieldsets must not change control dimensions.

Accessibility

  • Every Fieldset requires a FieldsetLegend that names the group.
  • Connect optional group descriptions with aria-describedby.
  • Keep each descendant control connected to its own FieldLabel and FieldError.
  • Do not use Fieldset only for visual layout when the fields are not semantically related.

Implementation Guidance

  • Group related controls with exactly one legend.
  • Do not place fields in a Fieldset without FieldsetLegend.
  • Preserve native fieldset and disabled semantics.

On This Page