Fieldsets
Fieldsets let you group related fields together with optional legends, descriptions, and grid layouts. A group with a legend renders as an HTML <fieldset> with a <legend>. A group without a legend renders as a <div> so it does not create an unnamed fieldset in the accessibility tree.
Basic Fieldset
Wrap fields in a Fieldset and give it a legend:
use InertiaUI\Forms\Fields\Fieldset;
use InertiaUI\Forms\Fields\TextInput;
use InertiaUI\Forms\Form;
class RegistrationForm extends Form
{
protected ?string $actionRoute = 'register';
public function fields(): array
{
return [
Fieldset::make()
->legend('Personal Information')
->fields([
TextInput::make('first_name')->required(),
TextInput::make('last_name')->required(),
TextInput::make('email')->email()->required(),
]),
];
}
}Legend and Description
Add a description below the legend for extra context:
Fieldset::make()
->legend('Shipping Address')
->description('Where should we send your order?')
->fields([
TextInput::make('street'),
TextInput::make('city'),
TextInput::make('state'),
TextInput::make('zip'),
]),The frontend renderers output fieldset descriptions as escaped text. Pass plain strings here; markup-looking strings are displayed as text.
Grid Layout with Columns
Use columns() to arrange fields in a CSS grid. The value is the maximum column target, and the grid automatically drops to fewer columns when the container is too narrow:
Fieldset::make()
->legend('Address')
->columns(2)
->fields([
TextInput::make('first_name'), // Column 1
TextInput::make('last_name'), // Column 2
TextInput::make('street'), // Column 1
TextInput::make('city'), // Column 2
TextInput::make('state'), // Column 1
TextInput::make('zip'), // Column 2
]),Fields flow left-to-right, top-to-bottom. For a layout that may use up to three columns:
Fieldset::make()
->columns(3)
->fields([
TextInput::make('city'),
Combobox::make('state')->options($states),
TextInput::make('zip'),
]),Multiple Fieldsets
A form may have multiple fieldsets, each with its own layout:
public function fields(): array
{
return [
Fieldset::make()
->legend('Account')
->fields([
TextInput::make('username')->required(),
TextInput::make('email')->email()->required(),
]),
Fieldset::make()
->legend('Profile')
->columns(2)
->fields([
TextInput::make('first_name'),
TextInput::make('last_name'),
Textarea::make('bio')->rows(3),
]),
Fieldset::make()
->legend('Preferences')
->fields([
Toggle::make('notifications'),
Toggle::make('newsletter'),
]),
Submit::make('Save'),
];
}Conditional Fieldsets
Fieldsets support the same conditional visibility API as fields. A hidden fieldset is removed with all of its children. Server-side validation also excludes each child field while the parent fieldset is hidden.
Fieldset::make()
->legend('Phone Details')
->visibleWhen('contact_method', 'phone')
->clearWhenHidden()
->fields([
TextInput::make('phone_number')->required(),
TextInput::make('phone_extension'),
]),Use clearWhenHidden() when child values should reset after the fieldset hides.
Fields Outside Fieldsets
Fields that are returned outside of any Fieldset are automatically wrapped in one. These two forms are equivalent:
// Explicit fieldset
public function fields(): array
{
return [
Fieldset::make()->fields([
TextInput::make('name'),
TextInput::make('email'),
]),
Submit::make('Save'),
];
}
// Auto-wrapped
public function fields(): array
{
return [
TextInput::make('name'),
TextInput::make('email'),
Submit::make('Save'),
];
}You may mix fieldsets and loose fields freely. Consecutive loose fields are grouped into the same auto-generated fieldset:
public function fields(): array
{
return [
TextInput::make('title'), // Auto-wrapped fieldset 1
TextInput::make('slug'), // Auto-wrapped fieldset 1
Fieldset::make() // Explicit fieldset 2
->legend('Details')
->columns(2)
->fields([
Combobox::make('category')->options($categories),
Combobox::make('status')->options($statuses),
]),
Textarea::make('body'), // Auto-wrapped fieldset 3
Submit::make('Publish'), // Auto-wrapped fieldset 3
];
}Custom CSS Classes
Add custom classes to a fieldset:
Fieldset::make()
->legend('Settings')
->class('rounded-lg bg-zinc-50 p-6')
->fields([...]),Fieldset ID
Set an HTML id attribute on the rendered fieldset wrapper, useful for anchor links or JavaScript targeting:
Fieldset::make()
->id('billing-section')
->legend('Billing Information')
->fields([...]),Using the Component Directly
Fieldset is exported from the root Vue and React packages. Pass the serialized fieldset shape, including its fields array. Visibility and child values use the surrounding form context, and each child is rendered through FieldRenderer.
<script setup lang="ts">
import { Fieldset } from '@inertiaui/form-vue'
import type { FormField } from '@inertiaui/form-vue'
const fields: FormField[] = [
{
component: 'TextInput',
name: 'billing_email',
label: 'Billing email',
inputType: 'email',
required: true,
precognitive: false,
disabled: false,
readonly: false,
autofocus: false,
clearable: false,
copyable: false,
viewable: false,
},
]
const trackFocus = () => {}
</script>
<template>
<Fieldset
:fields="fields"
legend="Billing"
description="Invoice contact details"
:columns="2"
id="billing-section"
class="rounded-lg bg-zinc-50 p-6"
wrapper-class="ring-1 ring-zinc-200"
:visibility="{ field: 'billing_method', operator: '=', value: 'invoice' }"
clear-when-hidden
data-testid="billing-fieldset"
@focusin="trackFocus"
/>
</template>import { Fieldset } from '@inertiaui/form-react'
import type { FormField } from '@inertiaui/form-react'
const fields: FormField[] = [
{
component: 'TextInput',
name: 'billing_email',
label: 'Billing email',
inputType: 'email',
required: true,
precognitive: false,
disabled: false,
readonly: false,
autofocus: false,
clearable: false,
copyable: false,
viewable: false,
},
]
export function BillingFieldset() {
const trackFocus = () => {}
return (
<Fieldset
fields={fields}
legend="Billing"
description="Invoice contact details"
columns={2}
id="billing-section"
class="rounded-lg bg-zinc-50 p-6"
wrapperClass="ring-1 ring-zinc-200"
visibility={{ field: 'billing_method', operator: '=', value: 'invoice' }}
clearWhenHidden
data-testid="billing-fieldset"
onFocus={trackFocus}
/>
)
}The direct component reads fields, legend, description, columns, id, class, wrapperClass, visibility, and clearWhenHidden. Native attributes and listeners target the rendered <fieldset> or fallback <div>. Visibility and child values require the surrounding form context. clearWhenHidden is applied by Form, Wizard, or an outer FieldRenderer when a visible fieldset becomes hidden; the direct Fieldset component only renders or hides the group.