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
| Guidance | Practices |
|---|---|
| Do | Stack fields vertically for most forms — it's the easiest to scan top to bottom. |
| Do | Nest a horizontal FormLayout inside a vertical one when fields naturally pair up, like First Name + Last Name or City + State + ZIP. |
| Do | Use horizontal-labels for settings pages where labels sit beside their inputs. |
| Don't | Use FormLayout for form state or submission — it's just layout. Wrap it in a <form> for that. |
| Don't | Put unrelated fields side by side in a horizontal layout — save it for fields that belong together. |
| Don't | Nest horizontal-labels inside another FormLayout — it uses CSS Grid and needs to be the outermost container. |
Anatomy
| Element | Description | |
|---|---|---|
| Form title | Heading that describes the purpose of the form. | |
| Fields | required | Input components with labels for collecting user data. |
| Footer | Contains confirmation buttons such as Submit or Cancel. |
Import
tsimport {XDSFormLayout} from '@xds/core/FormLayout'
Props
| Prop | Type | Description |
|---|---|---|
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 | ReactNode | Form fields to arrange. Accepts XDS inputs (XDSTextInput, XDSSelector, etc.) and XDSField-wrapped custom controls. |
xstyle | StyleXStyles | StyleX 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} /><XDSSelectorlabel="Role"value={role}onChange={v => setRole(v as string)}options={[{label: 'Viewer', value: 'viewer'},{label: 'Editor', value: 'editor'},{label: 'Admin', value: 'admin'},]}/><XDSCheckboxListlabel="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"><XDSTextInputlabel="Display Name"value={displayName}onChange={setDisplayName}/><XDSTextInput label="Email" value={email} onChange={setEmail} /><XDSSelectorlabel="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>);}