use-a11y-form 🤖
React form hooks with a11y inclusive providers
- 🚶 Super simple, accessible, multi-step + routed + tabbable forms friendly
- 🤖 Compulsory accessibility. Aria labels and other good a11y practices are done by default
- 💅 Headless. Integrates well with your existing components and stylings
Installation
npm i use-a11y-form
Getting started
import { useForm, Form } from 'use-a11y-form'
const SignIn = () => {
const form = useForm({
values: { email: '', password: '' },
validate: values => ({
name: !values.name && 'Enter a name',
password: !values.password && 'Enter a password',
}),
onSubmit: async (form, event) => {
return 'Signed in successfully'
},
})
return (
<Form form={form}>
<Form.Field name='name'>
<Form.Label>Name</Form.Label>
<Form.Input auto='name' />
<Form.Error />
</Form.Field>
<Form.Field name='email'>
<Form.Label>Email</Form.Label>
<Form.Input type='email' auto='email' />
<Form.Error />
</Form.Field>
<Form.Assertive />
<Form.Submit>Continue</Form.Submit>
</Form>
)
}
Component behavior outside of form context
Some form components can be used outside of a <Form/>
context. Wrapping your label and input field with no form context will automatically generate and assign ids.
const Field = ({ label, name, ...props }) => {
return (
<div className='...'>
<Form.Field>
{/* Will render label with correct id binding to your input */}
<Form.Label>{label}</Form.Label>
<Form.Input {...props} className={cx('...', props.className)} />
{/* Will be ignored outside <Form/> */}
<Form.Error />
</Form.Field>
</div>
)
}
<div>
<label for="XY"></label>
<input id="XY"></label>
</div>
API Reference
useForm
Creates a new form api
useForm({
values,
validate,
onSubmit,
translate,
disabled,
valuesDependency,
allowSubmitAttempt,
allowErrorSubmit,
})
Form API
const form = useForm(...)
form.errors
form.values
form.touched
form.alert
form.isSubmitting
form.isValid
form.isTouched
form.isCompleted
form.setValues({ name: value })
form.setValue("name", value)
form.setAssertive("Try again later.")
form.completed()
form.reset()
form.reset({ ... })
Validation
The validate function recieves the current values and returns an object mapping fields to errors. Only strings are considered errors.
useForm({
values: { name: '' },
validate: values => ({ name: !name && '' }),
})
Validate values using vx
vx
is a validation helper function that allows you to chain-compose multiple validations for a single value.
({ password }) => ({
password: vx(
!password && "Enter a name",
password.length < 8 && "Password a longer password",
)
}),
Submit
The submit function recieves the current form object and values
useForm({
onSubmit: async ({ values, ...form }) => {
const { error } = await signIn(values)
if (error) return error
form.complete()
},
})
Form providers
Form
Root form and context provider.
Parameters
form
: form api objectasChild
: use child as rendered elementvalidate
: whether to reenable standard html form validation
All form properties are also passed as data attributes: data-use-a11y-form
, data-submitting
, data-disabled
, data-submit-disabled
, data-touched
, data-completed
, data-valid
, data-invalid
, data-showing-errors
<Form form={form}>
{...}
</Form>
<Form form={form} asChild>
<form />
</Form>
Form.Context
Makes field context accessible through callback. Useful for custom or complex input components.
Will throw an error if used outside <Form/>
<Form form={form}>
<Form.Context>
{form => {
form.isCompleted
}}
</Form.Context>
</Form>
Form.FieldSet
Wraps fields in a accessible fieldset
<Form.FieldSet name="name">{...}</Form.FieldSet>
Form.Field
Wraps field components like Form.Input
and Form.Label
with field context
<Form.Field name="name">
{...}
</Form.Field>
Form.FieldContext
Makes field context accessible through callback. Useful for custom or complex input components.
Will throw an error if used outside <Form.Field/>
<Form.Field name='country'>
<Form.FieldContext<string>>{field => <select value={field.value} />}</Form.FieldContext>
</Form.Field>
Form.Label
Create a label inside a field context
Parameters
asChild
: use child as rendered element
<Form.Field name='name'>
<Form.Label>Name</Form.Label>
</Form.Field>
Form.Input
Create an input inside a field context
Parameters
asChild
: use child as rendered elementauto
: pass a typed and accessible autocomplete
All input properties are also passed as data attributes: data-error
, data-touched
, data-has-hidden-error
, data-disabled
, data-field-context
<Form.Field name='name'>
<Form.Input />
{}
<Form.Input /> {}
<Form.Input auto /> {}
<Form.Input auto={false} /> {}
<Form.Input auto='given-name' /> {}
</Form.Field>
Form.Error
Create an accessible input error inside a field context
Parameters
asChild
: use child as rendered element
<Form.Field name='name'>
<Form.Error />
</Form.Field>
Form.Assertive
Render assertive alert that may be returned from onSubmit
Parameters
asChild
: use child as rendered element
<Form form={form}>
<Form.Assertive />
{}
<Form.Assertive>Something went wrong</Form.Assertive>
</Form>
Form.Submit
Render provided form submit
Parameters
asChild
: use child as rendered element
<Form form={form}>
<Form.Submit />
</Form>
vx
Validation helper
vx('Error A', 'Error B')
vx(false, 'Error B')
vx(false, false)
Context hooks
⚠️ Context hooks will throw an error if used outside their respective providers.
useFormContext
Access your form's context from your component
const form = useFormContext()
useFieldContext
Access your a field's context from your component
const form = useFieldContext<string>()
form.value
Adapters
createFieldAdapter
Creates a new field to component props adapter
const adaptRadio = createFieldAdapter((field) => ({ ..., checked: field.value }))
adaptSelect
Adapts a field context to work with select input
<Form.Field name='country'>
<Form.FieldContext>
{field => {
field.value // null | "afghanistan" | ...
}}
</Form.FieldContext>
<Form.FieldContext<Options>>
{adaptSelect(field => {
field.value
})}
</Form.FieldContext>
</Form.Field>
adaptCheckbox
Adapts a field context to work with a checkbox input
<Form.Field name='accept'>
<Form.FieldContext<boolean>>
{adaptCheckbox(props => (
<input {...props} />
))}
</Form.FieldContext>
</Form.Field>