import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Conventions/Forms" summary="How to integrate the Design System with form libraries." />

# Forms

Design System form components work with form libraries such as
[react-hook-form](https://react-hook-form.com/). Which integration API you need depends on the
component's value interface.

## `register` or `Controller`?

Components that render a native input and expose `name`, `ref`, and `onChange` work with
`register` directly. Components with a value-based API (`value` / `onValueChange` or
`checked` / `onCheckedChange`) need `Controller`.

| Component                                                   | Integration                                  |
| ----------------------------------------------------------- | -------------------------------------------- |
| `TextField`, `PasswordField`, `TextareaField`               | `register`                                   |
| `NumberField`                                               | `register` (use `valueAsNumber`)             |
| `Checkbox`                                                  | `register`                                   |
| `SelectField`                                               | `Controller` (`value` / `onValueChange`)     |
| `Slider`, `RangeSlider`                                     | `Controller` (`value` / `onValueChange`)     |
| `Switch`                                                    | `Controller` (`checked` / `onCheckedChange`) |
| `SingleSelectChips`, `MultiSelectChips`, `DismissibleChips` | `Controller` (`value` / `onValueChange`)     |
| `RatingInput`                                               | `Controller` (`value` / `onValueChange`)     |

```tsx
// register: the field spreads directly
<TextField
    label="Full Name"
    {...register('fullName', { required: 'Full name is required' })}
    error={errors.fullName?.message}
    required
/>

// Controller: wire the field manually
<Controller
    name="country"
    control={control}
    rules={{ required: 'Country is required' }}
    render={({ field }) => (
        <SelectField
            ref={field.ref}
            label="Country"
            value={field.value}
            onValueChange={field.onChange}
            onBlur={field.onBlur}
            error={errors.country?.message}
            required
        >
            {/* options */}
        </SelectField>
    )}
/>
```

## Labels, errors, and required state

Field components (`TextField`, `SelectField`, …) take `label`, `error`, and `required` props
directly. Controls without a built-in label — `Slider`, `Switch`, `Checkbox`, Chips, `RatingInput`
— get them from a wrapping [FormControl](/docs/components-formcontrol--docs):

```tsx
<FormControl label="Experience level" error={errors.experienceLevel?.message}>
    <Slider value={field.value} onValueChange={field.onChange} aria-label="Experience level" />
</FormControl>
```

When the form library owns validation, disable the browser's built-in validation so error styling
stays consistent:

```tsx
<form onSubmit={handleSubmit(onSubmit)} noValidate>
```

## Complete example

A full registration form covering every field type above — including validation, reset, and
submission — runs as an
[executable interaction test](/story/conventions-forms-tests--integration-with-react-hook-form).
