AI Skill Library

React Hook Form

Performant forms: register, validation, errors, watch, dynamic fields, Zod integration.

reactformsfrontend
# React Hook Form

## Install
```bash
npm install react-hook-form
npm install @hookform/resolvers zod  # for Zod validation
```

## Basic form
```tsx
import { useForm } from 'react-hook-form'

type FormData = { email: string; password: string }

export default function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
    reset,
  } = useForm<FormData>()

  const onSubmit = async (data: FormData) => {
    await loginUser(data)
    reset()
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('email', {
          required: 'Email is required',
          pattern: { value: /^[^@]+@[^@]+$/, message: 'Invalid email' },
        })}
      />
      {errors.email && <span>{errors.email.message}</span>}

      <input
        type="password"
        {...register('password', { required: true, minLength: 8 })}
      />
      {errors.password && <span>Min 8 characters</span>}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Loading...' : 'Login'}
      </button>
    </form>
  )
}
```

## Zod resolver
```tsx
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const schema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
})
type FormData = z.infer<typeof schema>

const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
  resolver: zodResolver(schema),
})
```

## watch & setValue
```tsx
const { watch, setValue, getValues } = useForm()
const email = watch('email')           // single field
const allValues = watch()              // all fields

watch((data, { name }) => {            // subscribe to changes
  console.log(name, data[name])
})

setValue('role', 'admin', { shouldValidate: true })
```

## Dynamic fields (useFieldArray)
```tsx
import { useFieldArray } from 'react-hook-form'
const { fields, append, remove, move } = useFieldArray({
  control,
  name: 'items',
})

fields.map((field, i) => (
  <div key={field.id}>
    <input {...register(`items.${i}.name`)} />
    <button type="button" onClick={() => remove(i)}>Remove</button>
  </div>
))
<button type="button" onClick={() => append({ name: '' })}>Add</button>
```

## Controlled components (Controller)
```tsx
import { Controller } from 'react-hook-form'
<Controller
  name="date"
  control={control}
  render={({ field }) => (
    <DatePicker value={field.value} onChange={field.onChange} />
  )}
/>
```

API: /api/skills/react-hook-form