Fieldset
A composable fieldset for grouping related form controls under one accessible legend.
API Reference
Original primitive API
Behavior, accessibility details, and low-level props are documented by Base UI.
Basic
import { Fieldset, FieldsetLegend, Field, FieldLabel, FieldControl, FieldError,} from "moduix";export function FieldsetDemo() { return ( <Fieldset> <FieldsetLegend>Billing details</FieldsetLegend> <Field validationMode="onBlur"> <FieldLabel>Company</FieldLabel> <FieldControl required placeholder="Enter company name" /> <FieldError match="valueMissing">Please enter company name.</FieldError> </Field> <Field validationMode="onBlur"> <FieldLabel>Tax ID</FieldLabel> <FieldControl required placeholder="Enter tax ID" /> <FieldError match="valueMissing">Please enter tax ID.</FieldError> </Field> </Fieldset> );}Full list of component variables available for project-level overrides.
| Property | Default | Description |
|---|---|---|
| --fieldset-border-color | transparent | Controls the root fieldset border color. |
| --fieldset-border-style | solid | Controls the root fieldset border style. |
| --fieldset-border-width | 0 | Controls the root fieldset border width. |
| --fieldset-disabled-opacity | var(--opacity-disabled) | Controls disabled fieldset opacity. |
| --fieldset-gap | var(--spacing-4) | Controls spacing between fieldset parts. |
| --fieldset-legend-border-color | var(--color-border) | Controls legend bottom border color. |
| --fieldset-legend-border-style | solid | Controls legend bottom border style. |
| --fieldset-legend-border-width | var(--border-width-sm) | Controls legend bottom border width. |
| --fieldset-legend-color | var(--color-foreground) | Controls legend text color. |
| --fieldset-legend-font-size | var(--text-lg) | Controls legend font size. |
| --fieldset-legend-font-weight | var(--weight-semibold) | Controls legend font weight. |
| --fieldset-legend-line-height | var(--line-height-text-lg) | Controls legend line height. |
| --fieldset-legend-margin | 0 | Controls legend margin. |
| --fieldset-legend-padding | 0 0 var(--fieldset-legend-padding-bottom, var(--spacing-3)) | Controls legend padding. |
| --fieldset-legend-padding-bottom | var(--spacing-3) | Controls legend bottom padding. |
| --fieldset-margin | 0 | Controls the root fieldset margin. |
| --fieldset-max-width | none | Controls the root fieldset max width. |
| --fieldset-padding | 0 | Controls the root fieldset padding. |
| --fieldset-radius | var(--radius-none) | Controls the root fieldset corner radius. |
| --fieldset-width | 100% | Controls the root fieldset width. |
Interactive variables scoped for docs preview without changing size scale tokens.
| Property | Value | Default | Description |
|---|---|---|---|
| --fieldset-border-color | transparent | Controls root fieldset border color. | |
| --fieldset-gap | var(--spacing-4) | Controls spacing between fieldset parts. | |
| --fieldset-legend-border-color | var(--color-border) | Controls legend border color. | |
| --fieldset-legend-color | var(--color-foreground) | Controls legend text color. | |
| --fieldset-radius | var(--radius-none) | Controls fieldset corner radius. |
Anatomy
Fieldset groups related controls under one semantic legend. Keep the visible group label in
FieldsetLegend, then place related Field blocks (or composed rows like radio options) inside the
same root so native fieldset semantics and disabled behavior stay consistent.
Fieldset
├─ FieldsetLegend
└─ grouped content
├─ Field
│ ├─ FieldLabel
│ ├─ FieldControl
│ └─ FieldError
└─ Field / FieldItem rows (optional)<Fieldset>
<FieldsetLegend>Billing details</FieldsetLegend>
<Field validationMode="onBlur">
<FieldLabel>Company</FieldLabel>
<FieldControl required placeholder="Enter company name" />
<FieldError match="valueMissing">Please enter company name.</FieldError>
</Field>
</Fieldset>| Part | Role |
|---|---|
Fieldset | Root semantic group. Renders native fieldset, controls shared disabled state, and supports render. |
FieldsetLegend | Group label announced by assistive technologies for all nested controls in the fieldset. |
Fieldset does not have service slots such as portal, backdrop, or viewport.
In most cases, keep default semantic structure and customize visible parts (Fieldset,
FieldsetLegend, and nested field slots) with className and CSS variables.
Composition
Fieldset renders a native fieldset by default and forwards Base UI state, refs, className,
function className, style, function style, and render. Use render when the same root
needs to also be another Base UI component, such as RadioGroup.
Fieldset and FieldsetLegend are the only public parts. There are no hidden positioner,
backdrop, viewport, portal, or other service slots to style through classNames; pass className
directly to the part you want to customize.
<Fieldset className={styles.fieldset}>
<FieldsetLegend className={styles.legend}>Billing details</FieldsetLegend>
</Fieldset>The package exports Base UI state types for advanced styling callbacks:
import { type FieldsetState, type FieldsetLegendState } from 'moduix';Examples
Disabled
Use disabled on Fieldset to prevent interaction with every native control inside the group.
import { Fieldset, FieldsetLegend, Field, FieldLabel, FieldControl,} from "moduix";export function DisabledFieldsetDemo() { return ( <Fieldset disabled> <FieldsetLegend>Disabled account details</FieldsetLegend> <Field> <FieldLabel>Email</FieldLabel> <FieldControl defaultValue="team@example.com" /> </Field> <Field> <FieldLabel>Phone</FieldLabel> <FieldControl defaultValue="+1 (555) 123-45-67" /> </Field> </Fieldset> );}Radio Group
Use render to compose the fieldset semantics with another root component, such as RadioGroup.
import { Field, Fieldset, RadioGroup, FieldsetLegend, FieldItem, FieldLabel, Radio, RadioLabel,} from "moduix";export function RadioGroupFieldsetDemo() { return ( <Field name="storageType"> <Fieldset render={<RadioGroup defaultValue="ssd" />}> <FieldsetLegend>Storage type</FieldsetLegend> {storageTypes.map((item) => ( <FieldItem key={item.value}> <FieldLabel> <Radio value={item.value} /> <RadioLabel>{item.label}</RadioLabel> </FieldLabel> </FieldItem> ))} </Fieldset> </Field> );}const storageTypes = [ { value: "ssd", label: "SSD" }, { value: "hdd", label: "HDD" },];Custom Styles
Pass className to Fieldset, FieldsetLegend, and nested field slots when styling with CSS Modules, Tailwind CSS, or CSS-in-JS.
import { Fieldset, FieldsetLegend, Field, FieldLabel, FieldControl, FieldError,} from "moduix";export function StyledFieldsetDemo() { return ( <Fieldset className={styles.customFieldset}> <FieldsetLegend className={styles.customLegend}> Styled fieldset </FieldsetLegend> <Field validationMode="onBlur" className={styles.customField}> <FieldLabel className={styles.customLabel}>Project name</FieldLabel> <FieldControl required placeholder="Maps Platform" className={styles.customControl} /> <FieldError className={styles.customError} match="valueMissing"> Please enter a project name. </FieldError> </Field> </Fieldset> );}.customFieldset { max-width: 20rem; gap: var(--spacing-3); padding: var(--spacing-4); border: var(--border-width-sm) solid color-mix(in srgb, var(--color-primary) 30%, transparent); border-radius: var(--radius-lg);}.customLegend { border-color: color-mix(in srgb, var(--color-primary) 40%, transparent); color: var(--color-primary);}.customField { gap: var(--spacing-2);}.customLabel,.customError { color: var(--color-primary);}.customControl { border-color: color-mix(in srgb, var(--color-primary) 40%, transparent);}.customControl:focus { outline-color: var(--color-primary);}