Form
A form is a group of inputs that allows users to submit data to a server, with support for providing field validation errors.
install | yarn add react-aria-components |
---|---|
version | 1.1.1 |
usage | import {Form} from 'react-aria-components' |
Example#
import {Button, FieldError, Form, Input, Label, TextField} from 'react-aria-components';
<Form>
<TextField name="email" type="email" isRequired>
<Label>Email</Label>
<Input />
<FieldError />
</TextField>
<Button type="submit">Submit</Button>
</Form>
import {
Button,
FieldError,
Form,
Input,
Label,
TextField
} from 'react-aria-components';
<Form>
<TextField name="email" type="email" isRequired>
<Label>Email</Label>
<Input />
<FieldError />
</TextField>
<Button type="submit">Submit</Button>
</Form>
import {
Button,
FieldError,
Form,
Input,
Label,
TextField
} from 'react-aria-components';
<Form>
<TextField
name="email"
type="email"
isRequired
>
<Label>
Email
</Label>
<Input />
<FieldError />
</TextField>
<Button type="submit">
Submit
</Button>
</Form>
Show CSS
.react-aria-Form {
display: flex;
flex-direction: column;
align-items: start;
gap: 8px;
}
.react-aria-Form {
display: flex;
flex-direction: column;
align-items: start;
gap: 8px;
}
.react-aria-Form {
display: flex;
flex-direction: column;
align-items: start;
gap: 8px;
}
Features#
The HTML <form> element can be used to build forms. React Aria's Form
component extends HTML forms with support for providing server-side validation errors to the fields within it.
- Accessible – Uses a native
<form>
element, with support for ARIA labelling to create a form landmark. - Validation – Support for native HTML constraint validation with customizable UI, custom validation functions, realtime validation, and server-side validation errors.
See the Forms guide to learn more about React Aria's form components, including submitting data, and form validation techniques.
Anatomy#
A form consists of a container element that includes a group of input elements, typically with a button the user can press to submit data to a server. Forms may also include validation error messages, and a button to reset the form data to its initial state.
If a form has an aria-label
or aria-labelledby
attribute, it is exposed to assistive technology as a form landmark, allowing users to quickly navigate to it.
import {Form, Button} from 'react-aria-components';
<Form>
{/* ... */}
<Button type="submit" />
<Button type="reset" />
</Form>
import {Form, Button} from 'react-aria-components';
<Form>
{/* ... */}
<Button type="submit" />
<Button type="reset" />
</Form>
import {
Button,
Form
} from 'react-aria-components';
<Form>
{/* ... */}
<Button type="submit" />
<Button type="reset" />
</Form>
Starter kits#
To help kick-start your project, we offer starter kits that include example implementations of all React Aria components with various styling solutions. All components are fully styled, including support for dark mode, high contrast mode, and all UI states. Each starter comes with a pre-configured Storybook that you can experiment with, or use as a starting point for your own component library.
Events#
The onSubmit
event will be triggered when a user submits the form with the Enter key or by pressing a submit button. The onReset
event will be triggered when a user presses a reset button.
function Example() {
let [action, setAction] = React.useState(null);
return (
<Form
onSubmit={e => {
e.preventDefault();
let data = Object.fromEntries(new FormData(e.currentTarget));
setAction(`submit `);
}}
onReset={() => setAction('reset')} >
<TextField name="username" isRequired>
<Label>Username</Label>
<Input />
<FieldError />
</TextField>
<TextField name="password" type="password" isRequired>
<Label>Password</Label>
<Input />
<FieldError />
</TextField>
<div style={{display: 'flex', gap: 8}}>
<Button type="submit">Submit</Button>
<Button type="reset">Reset</Button> </div>
{action && <div>Action: <code>{action}</code></div>}
</Form>
);
}
function Example() {
let [action, setAction] = React.useState(null);
return (
<Form
onSubmit={(e) => {
e.preventDefault();
let data = Object.fromEntries(
new FormData(e.currentTarget)
);
setAction(`submit `);
}}
onReset={() => setAction('reset')} >
<TextField name="username" isRequired>
<Label>Username</Label>
<Input />
<FieldError />
</TextField>
<TextField name="password" type="password" isRequired>
<Label>Password</Label>
<Input />
<FieldError />
</TextField>
<div style={{ display: 'flex', gap: 8 }}>
<Button type="submit">Submit</Button>
<Button type="reset">Reset</Button> </div>
{action && (
<div>
Action: <code>{action}</code>
</div>
)}
</Form>
);
}
function Example() {
let [
action,
setAction
] = React.useState(
null
);
return (
<Form
onSubmit={(e) => {
e.preventDefault();
let data = Object
.fromEntries(
new FormData(
e.currentTarget
)
);
setAction(
`submit
`);
}}
onReset={() =>
setAction(
'reset'
)} >
<TextField
name="username"
isRequired
>
<Label>
Username
</Label>
<Input />
<FieldError />
</TextField>
<TextField
name="password"
type="password"
isRequired
>
<Label>
Password
</Label>
<Input />
<FieldError />
</TextField>
<div
style={{
display:
'flex',
gap: 8
}}
>
<Button type="submit">
Submit
</Button>
<Button type="reset">
Reset
</Button> </div>
{action && (
<div>
Action:{' '}
<code>
{action}
</code>
</div>
)}
</Form>
);
}
Validation#
React Aria supports native HTML constraint validation with customizable UI, custom validation functions, realtime validation, and integration with server-side validation errors. The Form
component facilitates server-side validation by providing error messages to the fields within it.
To provide validation errors, the validationErrors
prop should be set to an object that maps each field's name
prop to a string or array of strings representing one or more errors. These are displayed to the user as soon as the validationErrors
prop is set, and cleared after the user modifies each field's value.
<Form validationErrors={{username: 'Sorry, this username is taken.'}}>
<TextField name="username">
<Label>Username</Label>
<Input />
<FieldError />
</TextField>
</Form>
<Form
validationErrors={{
username: 'Sorry, this username is taken.'
}}
>
<TextField name="username">
<Label>Username</Label>
<Input />
<FieldError />
</TextField>
</Form>
<Form
validationErrors={{
username:
'Sorry, this username is taken.'
}}
>
<TextField name="username">
<Label>
Username
</Label>
<Input />
<FieldError />
</TextField>
</Form>
See the Forms guide to learn more about form validation in React Aria, including client-side validation, and integration with other frameworks and libraries.
Focus management#
By default, after a user submits a form with validation errors, the first invalid field will be focused. You can prevent this by calling preventDefault
during the onInvalid
event, and move focus yourself. This example shows how to move focus to an alert element at the top of a form.
function Example() {
let [isInvalid, setInvalid] = React.useState(false);
return (
<Form
onInvalid={e => {
e.preventDefault();
setInvalid(true);
}} onSubmit={e => {
e.preventDefault();
setInvalid(false);
}}
onReset={() => setInvalid(false)}>
{isInvalid &&
<div role="alert" tabIndex={-1} ref={e => e?.focus()}> <h3>Unable to submit</h3>
<p>Please fix the validation errors below, and re-submit the form.</p>
</div>
}
<TextField name="firstName" isRequired>
<Label>First Name</Label>
<Input />
<FieldError />
</TextField>
<TextField name="lastName" isRequired>
<Label>Last Name</Label>
<Input />
<FieldError />
</TextField>
<div style={{display: 'flex', gap: 8}}>
<Button type="submit">Submit</Button>
<Button type="reset">Reset</Button>
</div>
</Form>
);
}
function Example() {
let [isInvalid, setInvalid] = React.useState(false);
return (
<Form
onInvalid={(e) => {
e.preventDefault();
setInvalid(true);
}} onSubmit={(e) => {
e.preventDefault();
setInvalid(false);
}}
onReset={() => setInvalid(false)}
>
{isInvalid && <div
role="alert"
tabIndex={-1}
ref={(e) => e?.focus()}
> <h3>Unable to submit</h3>
<p>
Please fix the validation errors below, and
re-submit the form.
</p>
</div>}
<TextField name="firstName" isRequired>
<Label>First Name</Label>
<Input />
<FieldError />
</TextField>
<TextField name="lastName" isRequired>
<Label>Last Name</Label>
<Input />
<FieldError />
</TextField>
<div style={{ display: 'flex', gap: 8 }}>
<Button type="submit">Submit</Button>
<Button type="reset">Reset</Button>
</div>
</Form>
);
}
function Example() {
let [
isInvalid,
setInvalid
] = React.useState(
false
);
return (
<Form
onInvalid={(e) => {
e.preventDefault();
setInvalid(true);
}} onSubmit={(e) => {
e.preventDefault();
setInvalid(
false
);
}}
onReset={() =>
setInvalid(
false
)}
>
{isInvalid && <div
role="alert"
tabIndex={-1}
ref={(e) =>
e?.focus()}
> <h3>
Unable to
submit
</h3>
<p>
Please fix
the
validation
errors below,
and re-submit
the form.
</p>
</div>}
<TextField
name="firstName"
isRequired
>
<Label>
First Name
</Label>
<Input />
<FieldError />
</TextField>
<TextField
name="lastName"
isRequired
>
<Label>
Last Name
</Label>
<Input />
<FieldError />
</TextField>
<div
style={{
display:
'flex',
gap: 8
}}
>
<Button type="submit">
Submit
</Button>
<Button type="reset">
Reset
</Button>
</div>
</Form>
);
}
Show CSS
.react-aria-Form [role=alert] {
border: 2px solid var(--invalid-color);
background: var(--overlay-background);
border-radius: 6px;
padding: 12px;
max-width: 250px;
outline: none;
&:focus-visible {
outline: 2px solid var(--focus-ring-color);
outline-offset: 2px;
}
h3 {
margin-top: 0;
}
p {
margin-bottom: 0;
}
}
.react-aria-Form [role=alert] {
border: 2px solid var(--invalid-color);
background: var(--overlay-background);
border-radius: 6px;
padding: 12px;
max-width: 250px;
outline: none;
&:focus-visible {
outline: 2px solid var(--focus-ring-color);
outline-offset: 2px;
}
h3 {
margin-top: 0;
}
p {
margin-bottom: 0;
}
}
.react-aria-Form [role=alert] {
border: 2px solid var(--invalid-color);
background: var(--overlay-background);
border-radius: 6px;
padding: 12px;
max-width: 250px;
outline: none;
&:focus-visible {
outline: 2px solid var(--focus-ring-color);
outline-offset: 2px;
}
h3 {
margin-top: 0;
}
p {
margin-bottom: 0;
}
}
Props#
Name | Type | Default | Description |
validationBehavior | 'aria' | 'native' | 'native' | Whether to use native HTML form validation to prevent form submission when a field value is missing or invalid, or mark fields as required or invalid via ARIA. |
validationErrors | ValidationErrors | — | Validation errors for the form, typically returned by a server. This should be set to an object mapping from input names to errors. |
action | string | FormHTMLAttributes<HTMLFormElement>['action'] | — | Where to send the form-data when the form is submitted. See MDN. |
encType | 'application/x-www-form-urlencoded'
| 'multipart/form-data'
| 'text/plain' | — | The enctype attribute specifies how the form-data should be encoded when submitting it to the server. See MDN. |
method | 'get'
| 'post'
| 'dialog' | — | The HTTP method to submit the form with. See MDN. |
target | '_blank'
| '_self'
| '_parent'
| '_top' | — | The target attribute specifies a name or a keyword that indicates where to display the response that is received after submitting the form. See MDN. |
autoComplete | 'off' | 'on' | — | Indicates whether input elements can by default have their values automatically completed by the browser. See MDN. |
autoCapitalize | 'off'
| 'none'
| 'on'
| 'sentences'
| 'words'
| 'characters' | — | Controls whether inputted text is automatically capitalized and, if so, in what manner. See MDN. |
children | ReactNode | — | The children of the component. |
className | string | — | The CSS className for the element. |
style | CSSProperties | — | The inline style for the element. |
Events
Name | Type | Description |
onSubmit | (
(event: FormEvent<HTMLFormElement>
)) => void | Triggered when a user submits the form. |
onReset | (
(event: FormEvent<HTMLFormElement>
)) => void | Triggered when a user resets the form. |
onInvalid | (
(event: FormEvent<HTMLFormElement>
)) => void | Triggered for each invalid field when a user submits the form. |
Accessibility
Name | Type | Description |
role | 'search' | 'presentation' | An ARIA role override to apply to the form element. |
id | string | The element's unique identifier. See MDN. |
aria-label | string | Defines a string value that labels the current element. |
aria-labelledby | string | Identifies the element (or elements) that labels the current element. |
aria-describedby | string | Identifies the element (or elements) that describes the object. |
aria-details | string | Identifies the element (or elements) that provide a detailed, extended description for the object. |
Styling#
React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin className
attribute which can be targeted using CSS selectors. These follow the react-aria-ComponentName
naming convention.
.react-aria-Form {
/* ... */
}
.react-aria-Form {
/* ... */
}
.react-aria-Form {
/* ... */
}
A custom className
can also be specified on any component. This overrides the default className
provided by React Aria with your own.
<Form className="my-form">
{/* ... */}
</Form>
<Form className="my-form">
{/* ... */}
</Form>
<Form className="my-form">
{/* ... */}
</Form>
Advanced customization#
Validation context#
The Form
component provides a value for FormValidationContext
, which allows child elements to receive validation errors from the form. You can provide a value for this context directly in case you need to customize the form element, or reuse an existing form component.
import {FormValidationContext} from 'react-aria-components';
<form>
<FormValidationContext.Provider
value={{ username: 'Sorry, this username is taken.' }}
>
<TextField name="username">
<Label>Username</Label>
<Input />
<FieldError />
</TextField>
</FormValidationContext.Provider>
</form>
import {FormValidationContext} from 'react-aria-components';
<form>
<FormValidationContext.Provider
value={{ username: 'Sorry, this username is taken.' }}
>
<TextField name="username">
<Label>Username</Label>
<Input />
<FieldError />
</TextField>
</FormValidationContext.Provider>
</form>
import {FormValidationContext} from 'react-aria-components';
<form>
<FormValidationContext.Provider
value={{
username:
'Sorry, this username is taken.'
}}
>
<TextField name="username">
<Label>
Username
</Label>
<Input />
<FieldError />
</TextField>
</FormValidationContext.Provider>
</form>
Custom children#
You can also consume FormValidationContext
in your own custom form input components to receive validation errors. This example shows a native <select>
that displays validation errors provided by Form
.
import type {SelectHTMLAttributes} from 'react';
import {useContext} from 'react';
import {useId} from 'react-aria';
function NativeSelect(
props: SelectHTMLAttributes<HTMLSelectElement> & { label: string }
) {
let errors = useContext(FormValidationContext);
let error = errors?.[props.name];
let id = useId();
let descriptionId = useId();
return (
<div className="flex">
<label htmlFor={id}>{props.label}</label>
<select {...props} id={id} aria-describedby={descriptionId} />
<small className="invalid" id={descriptionId}>{error}</small>
</div>
);
}
<Form validationErrors={{ frequency: 'Please select a frequency.' }}>
<NativeSelect label="Frequency" name="frequency">
<option value="">Select an option...</option>
<option>Always</option>
<option>Sometimes</option>
<option>Never</option>
</NativeSelect>
</Form>
import type {SelectHTMLAttributes} from 'react';
import {useContext} from 'react';
import {useId} from 'react-aria';
function NativeSelect(
props: SelectHTMLAttributes<HTMLSelectElement> & {
label: string;
}
) {
let errors = useContext(FormValidationContext);
let error = errors?.[props.name];
let id = useId();
let descriptionId = useId();
return (
<div className="flex">
<label htmlFor={id}>{props.label}</label>
<select
{...props}
id={id}
aria-describedby={descriptionId}
/>
<small className="invalid" id={descriptionId}>
{error}
</small>
</div>
);
}
<Form
validationErrors={{
frequency: 'Please select a frequency.'
}}
>
<NativeSelect label="Frequency" name="frequency">
<option value="">Select an option...</option>
<option>Always</option>
<option>Sometimes</option>
<option>Never</option>
</NativeSelect>
</Form>
import type {SelectHTMLAttributes} from 'react';
import {useContext} from 'react';
import {useId} from 'react-aria';
function NativeSelect(
props:
& SelectHTMLAttributes<
HTMLSelectElement
>
& { label: string }
) {
let errors =
useContext(
FormValidationContext
);
let error = errors
?.[props.name];
let id = useId();
let descriptionId =
useId();
return (
<div className="flex">
<label
htmlFor={id}
>
{props.label}
</label>
<select
{...props}
id={id}
aria-describedby={descriptionId}
/>
<small
className="invalid"
id={descriptionId}
>
{error}
</small>
</div>
);
}
<Form
validationErrors={{
frequency:
'Please select a frequency.'
}}
>
<NativeSelect
label="Frequency"
name="frequency"
>
<option value="">
Select an
option...
</option>
<option>
Always
</option>
<option>
Sometimes
</option>
<option>
Never
</option>
</NativeSelect>
</Form>