Form
Preview: Validation
Native <form> with React Aria validation: field-level `validate`, `isRequired`, and server errors via `validationErrors={{ fieldName: 'message' }}`.
Import
import { Form, FormActions } from '@marbleui/ui'Props
<FormActions>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | ReactNode | — | |
| className | string | — |
Accessibility
Keyboard interaction, focus management and ARIA semantics come from the underlying primitive: React Aria Form. Follow the primitive's docs for the full keyboard map.
Usage notes
Native <form> with React Aria validation. Fields validate on blur and on submit; the first invalid field receives focus.
<Form onSubmit={submit} validationErrors={serverErrors}>
<TextField name="email" label="Email" type="email" isRequired />
<FormActions><Button type="submit">Save</Button></FormActions>
</Form>
validationErrors={{ email: 'Already taken' }}shows server-side errors on the matchingname.layout="stack"stacks fields; useFieldsetto group related fields under a legend.- Read values from
FormDatainonSubmit(calle.preventDefault()for client-side handling).
Example source · show· hide
import { useState } from 'react'
import { Button } from '../button'
import { Checkbox } from '../checkbox'
import { Select, SelectItem } from '../select'
import { TextField } from '../text-field'
import { Form, FormActions } from './form'
function SignUp() {
const [errors, setErrors] = useState<Record<string, string>>({})
const [pending, setPending] = useState(false)
return (
<Form
className="w-80"
validationErrors={errors}
onSubmit={(e) => {
e.preventDefault()
const data = Object.fromEntries(new FormData(e.currentTarget))
setPending(true)
setTimeout(() => {
setPending(false)
setErrors(data.email === 'taken@acme.com' ? { email: 'An account with this email already exists.' } : {})
}, 700)
}}
>
<TextField name="name" label="Full name" isRequired />
<TextField name="email" type="email" label="Work email" isRequired description='Try "taken@acme.com" for a server error.' />
<Checkbox name="terms" value="yes" isRequired>I agree to the terms</Checkbox>
<FormActions>
<Button type="reset" variant="ghost">Reset</Button>
<Button type="submit" isPending={pending}>Create account</Button>
</FormActions>
</Form>
)
}
export const styles = {
Validation: SignUp,
Grid: () => (
<Form layout="grid" className="w-[30rem]" onSubmit={(e) => e.preventDefault()}>
<TextField name="first" label="First name" />
<TextField name="last" label="Last name" />
<TextField name="company" label="Company" className="col-span-2" />
<Select name="size" label="Company size" className="col-span-2" defaultSelectedKey="11">
<SelectItem id="1">1–10</SelectItem>
<SelectItem id="11">11–50</SelectItem>
<SelectItem id="51">51–200</SelectItem>
</Select>
<FormActions>
<Button type="submit">Save</Button>
</FormActions>
</Form>
),
}Source: packages/ui/src/form/form.tsx
Catalog
- Category
- inputs
- Priority
- Core
- Primitive
- react-aria
- React Aria
- Form
- Base UI
- Form