# 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 = () => (
Username This will be your public display name.
) ``` ### 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 = () => (
Username Username is required.
) ``` ### 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 = () => (
Username (required) Username is required.
) ``` 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 = () => (
{/* Input */} Email {/* Textarea */} Bio {/* Select */} Country
) ``` ### 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 (
I agree to the terms {/* Clicking reset will restore checkbox to defaultChecked state */}
) } ``` ## 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 (
First Name {errors.firstName?.message} Email {errors.email?.message}
) } ``` ### 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 (
console.log(data))}> ( onChange(e.value[0])} onInteractOutside={() => onBlur()} > Framework {collection.items.map((item) => ( {item} ))} {error?.message} )} /> ) } ``` ## 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 (
{ e.preventDefault() e.stopPropagation() form.handleSubmit() }} > (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} ))} )} /> ) } ```