XDSFormLayout@xds/core · FormLayout

Usage

A layout container that arranges form fields with consistent spacing and direction. FormLayout handles where fields go — not state or submission. Wrap it in a <form> for that. Supports vertical (default), horizontal, and horizontal-labels directions, and can be nested to mix them.

Best practices

GuidancePractices
DoStack fields vertically for most forms — it's the easiest to scan top to bottom.
DoNest a horizontal FormLayout inside a vertical one when fields naturally pair up, like First Name + Last Name or City + State + ZIP.
DoUse horizontal-labels for settings pages where labels sit beside their inputs.
Don'tUse FormLayout for form state or submission — it's just layout. Wrap it in a <form> for that.
Don'tPut unrelated fields side by side in a horizontal layout — save it for fields that belong together.
Don'tNest horizontal-labels inside another FormLayout — it uses CSS Grid and needs to be the outermost container.

Anatomy

ElementDescription
Form titleHeading that describes the purpose of the form.
FieldsrequiredInput components with labels for collecting user data.
FooterContains confirmation buttons such as Submit or Cancel.

Import

ts
import {XDSFormLayout} from '@xds/core/FormLayout'

Props

PropTypeDescription
direction
'vertical' | 'horizontal' | 'horizontal-labels' (default: 'vertical')Controls field arrangement. Vertical stacks top-to-bottom, horizontal arranges left-to-right with equal flex-grow, and horizontal-labels uses CSS Grid with labels to the left of inputs (collapses to vertical on narrow viewports <=480px).
children
ReactNodeForm fields to arrange. Accepts XDS inputs (XDSTextInput, XDSSelector, etc.) and XDSField-wrapped custom controls.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}.

Examples

Common configurations, variations, and states.
FormLayout — HorizontalTwo fields side by side for naturally paired inputs like first and last name
tsx
'use client';
import {useState} from 'react';
import {XDSFormLayout} from '@xds/core/FormLayout';
import {XDSTextInput} from '@xds/core/TextInput';
export default function FormLayoutHorizontal() {
const [first, setFirst] = useState('Jordan');
const [last, setLast] = useState('Rivera');
return (
<XDSFormLayout direction="horizontal">
<XDSTextInput label="First Name" value={first} onChange={setFirst} />
<XDSTextInput label="Last Name" value={last} onChange={setLast} />
</XDSFormLayout>
);
}
FormLayout — Mixed ControlsForm with different control types — text input, selector, and checkboxes
tsx
'use client';
import {useState} from 'react';
import {XDSFormLayout} from '@xds/core/FormLayout';
import {XDSTextInput} from '@xds/core/TextInput';
import {XDSSelector} from '@xds/core/Selector';
import {XDSCheckboxList, XDSCheckboxListItem} from '@xds/core/CheckboxList';
export default function FormLayoutMixedControls() {
const [name, setName] = useState('Maya Torres');
const [role, setRole] = useState('editor');
const [notifications, setNotifications] = useState(['email', 'push']);
return (
<XDSFormLayout>
<XDSTextInput label="Full Name" value={name} onChange={setName} />
<XDSSelector
label="Role"
value={role}
onChange={v => setRole(v as string)}
options={[
{label: 'Viewer', value: 'viewer'},
{label: 'Editor', value: 'editor'},
{label: 'Admin', value: 'admin'},
]}
/>
<XDSCheckboxList
label="Notifications"
value={notifications}
onChange={setNotifications}>
<XDSCheckboxListItem label="Email" value="email" />
<XDSCheckboxListItem label="SMS" value="sms" />
<XDSCheckboxListItem label="Push" value="push" />
</XDSCheckboxList>
</XDSFormLayout>
);
}
FormLayout — Nested Address FormAddress form mixing vertical and horizontal layouts for grouped fields
tsx
'use client';
import {useState} from 'react';
import {XDSFormLayout} from '@xds/core/FormLayout';
import {XDSTextInput} from '@xds/core/TextInput';
export default function FormLayoutNested() {
const [first, setFirst] = useState('Priya');
const [last, setLast] = useState('Sharma');
const [email, setEmail] = useState('priya.sharma@example.com');
const [city, setCity] = useState('San Francisco');
const [state, setState] = useState('CA');
const [zip, setZip] = useState('94105');
return (
<XDSFormLayout>
<XDSFormLayout direction="horizontal">
<XDSTextInput label="First Name" value={first} onChange={setFirst} />
<XDSTextInput label="Last Name" value={last} onChange={setLast} />
</XDSFormLayout>
<XDSTextInput label="Email" value={email} onChange={setEmail} />
<XDSFormLayout direction="horizontal">
<XDSTextInput label="City" value={city} onChange={setCity} />
<XDSTextInput label="State" value={state} onChange={setState} />
<XDSTextInput label="ZIP" value={zip} onChange={setZip} />
</XDSFormLayout>
</XDSFormLayout>
);
}
FormLayout — Settings FormSettings form with labels placed beside their inputs
tsx
'use client';
import {useState} from 'react';
import {XDSFormLayout} from '@xds/core/FormLayout';
import {XDSTextInput} from '@xds/core/TextInput';
import {XDSSelector} from '@xds/core/Selector';
export default function FormLayoutHorizontalLabels() {
const [displayName, setDisplayName] = useState('Jane Doe');
const [email, setEmail] = useState('jane@example.com');
const [timezone, setTimezone] = useState('America/Los_Angeles');
return (
<XDSFormLayout direction="horizontal-labels">
<XDSTextInput
label="Display Name"
value={displayName}
onChange={setDisplayName}
/>
<XDSTextInput label="Email" value={email} onChange={setEmail} />
<XDSSelector
label="Timezone"
value={timezone}
onChange={v => setTimezone(v as string)}
options={[
{label: 'Pacific Time', value: 'America/Los_Angeles'},
{label: 'Eastern Time', value: 'America/New_York'},
{label: 'UTC', value: 'UTC'},
]}
/>
</XDSFormLayout>
);
}

Showcase source

tsx
'use client';
import {XDSFormLayout} from '@xds/core/FormLayout';
import {XDSTextInput} from '@xds/core/TextInput';
export default function FormLayoutShowcase() {
return (
<XDSFormLayout>
<XDSTextInput label="Name" value="" onChange={() => {}} />
<XDSTextInput label="Email" value="" onChange={() => {}} />
<XDSTextInput label="Bio" value="" onChange={() => {}} />
</XDSFormLayout>
);
}