# Forms
URL: https://ark-ui.com/docs/guides/forms
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/guides/forms.mdx
A guide to building forms with Ark UI components.
---
Ark UI provides the `Field` and `Fieldset` components for integrating with native `form` element or popular form
libraries like [React Hook Form](https://react-hook-form.com/), [TanStack Form](https://tanstack.com/form/latest), and
[Vee Validate](https://vee-validate.logaretm.com).
## Field Context
Form components in Ark UI automatically integrate with `Field` through context. When nested inside a `Field.Root`, they
inherit `disabled`, `invalid`, `required`, and `readOnly` states automatically.
```tsx
import { Field } from '@ark-ui/react/field'
import { NumberInput } from '@ark-ui/react/number-input'
const Demo = () => (
{/* NumberInput will be disabled */}
)
```
### Accessible Labels
When building accessible forms, you need to ensure that they are properly labeled and described.
- `Field.Label`: Used to provide an accessible label the input.
- `Field.HelperText`: Used to provide additional instructions about the input.
These components are automatically linked to the input element via the `aria-describedby` attribute.
> **Best practice**: Make sure that labels are visible (and not just used as placeholders) for screen readers to read
> them.
```tsx
import { Field } from '@ark-ui/react/field'
const Demo = () => (
)
```
### Error Handling and Validation
When the input is invalid, you can use the `Field.ErrorText` component to provide an error message for the input, and
pass the `invalid` prop to the `Field.Root` component.
> **Best practice**: Make sure to provide clear, specific error messages that are easy to understand and fix.
```tsx
import { Field } from '@ark-ui/react/field'
const Demo = () => (
)
```
### Required Fields
To indicate that a field is required, you can pass the `required` prop to the `Field.Root` component. Optionally, you
can use the `Field.RequiredIndicator` component to indicate that the field is required.
> **Best practice**: Don't rely solely on color to indicate required status
```tsx
import { Field } from '@ark-ui/react/field'
export const Demo = () => (
)
```
To indicate that a field is optional, use the `fallback` prop on the `Field.RequiredIndicator` component.
```tsx
(required)
```
### Native Controls
Field supports native HTML form controls including `input`, `textarea`, and `select`:
```tsx
import { Field } from '@ark-ui/react/field'
const Demo = () => (
)
```
### Form Reset
When the `reset` event is triggered on a form, all Ark UI components automatically sync their internal state with the
form's reset values.
> **Note**: For this to work correctly, always include the `HiddenInput` component in your form controls. The hidden
> input participates in the native form reset mechanism, and Ark UI listens for this to sync the component state.
```tsx
import { Checkbox } from '@ark-ui/react/checkbox'
const Demo = () => {
return (
)
}
```
## Fieldset Context
When you have multiple fields in a form or a component that renders multiple `input` elements, you can use the
`Fieldset` component to group them together.
Common use cases checkbox group, radio group, input + select composition, etc.
### Checkbox Group
```tsx
import { Fieldset } from '@ark-ui/react/fieldset'
import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
const Demo = () => (
Frameworks
{items.map((item) => (
))}
Choose your preferred frameworks
)
```
### Radio Group
```tsx
import { Fieldset } from '@ark-ui/react/fieldset'
import { RadioGroup } from '@ark-ui/react/radio-group'
import { RadioIcon } from 'lucide-react'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
const Demo = () => (
Frameworks
{items.map((item) => (
))}
Choose your preferred framework
)
```
## React Hook Form
Ark UI integrates seamlessly with React Hook Form. Use the `register` function for simple inputs and `Controller` for
complex components.
### Native Controls
```tsx
import { Field } from '@ark-ui/react/field'
import { useForm } from 'react-hook-form'
interface FormValues {
firstName: string
email: string
}
const Demo = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
const onSubmit = (data: FormValues) => console.log(data)
return (
)
}
```
### Custom Components
Use the `Controller` hook to integrate custom form components like select or combobox.
> **Best practice**: To ensure `react-hook-form` moves focus to invalid field control when performing validation,
> forward the field's `ref` to the respective Ark UI component.
```tsx
import { Field } from '@ark-ui/react/field'
import { Select, createListCollection } from '@ark-ui/react/select'
import { Controller, useForm } from 'react-hook-form'
const Demo = () => {
const { control, handleSubmit } = useForm({
defaultValues: { framework: '' },
})
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
return (
)
}
```
## TanStack Form
TanStack Form provides powerful form state management that works well with Ark UI.
### Native Controls
```tsx
import { useForm } from '@tanstack/react-form'
const form = useForm({
defaultValues: { firstName: '' },
})
const Demo = () => (
(
First Name field.handleChange(e.target.value)}
onBlur={field.handleBlur}
/>
{field.state.meta.errors.join(',')}
)}
/>
)
```
### Custom Components
Here's an example of how to integrate custom components like the select or combobox with Tanstack's `form.Field`
component.
```tsx
import { Field } from '@ark-ui/react/field'
import { Select, createListCollection } from '@ark-ui/react/select'
import { useForm } from '@tanstack/react-form'
const Demo = () => {
const form = useForm({
defaultValues: { username: '' },
})
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
return (
(value.length < 3 ? 'Username must be at least 3 characters' : undefined),
}}
children={(field) => (
0}>
Username field.handleChange(e.value[0])}
onInteractOutside={() => field.handleBlur()}
>
Username
{collection.items.map((item) => (
{item}
))}
)}
/>
)
}
```