Skip to content
Development
Skill

/web-forms-react-hook-form

React Hook Form patterns - useForm, Controller, useFieldArray, validation resolver, performance optimization

From plugin
agents-inc-skills
24200 skills
Install
$ npx -y skills add agents-inc/skills --skill web-forms-react-hook-form --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-forms-react-hook-form

Context preview

The summary Claude sees to decide when to auto-load this skill.

React Hook Form patterns - useForm, Controller, useFieldArray, validation resolver, performance optimization

SKILL.md

web-forms-react-hook-form.SKILL.md
name: web-forms-react-hook-form
description: React Hook Form patterns - useForm, Controller, useFieldArray, validation resolver, performance optimization

React Hook Form Patterns

> **Quick Guide:** `register` binds native inputs and keeps them uncontrolled; `Controller` wraps > components that hold their own value; `useFieldArray` drives repeatable rows and is keyed on > `field.id`. Validation arrives either as `register` rules or as a schema through `resolver`. > Re-renders are the thing to watch: `useWatch` and `useFormState` subscribe to named fields, > whereas `watch()` and a wide `formState` destructure subscribe to the whole form.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — a type-safe form with `register`, error display and accessibility attributes
  • [examples/controlled-components.md](examples/controlled-components.md) — `Controller` around a select, a date picker and a checkbox group
  • [examples/validation.md](examples/validation.md) — wiring a schema in through `resolver`
  • [examples/arrays.md](examples/arrays.md) — `useFieldArray` line items with a live total
  • [examples/performance.md](examples/performance.md) — isolated subscriptions, `FormStateSubscribe`, `useWatch` `exact` and `compute`
  • [examples/form-options.md](examples/form-options.md) — the `values` prop for async data, `disabled`, the `<Form />` component
  • [examples/wizard.md](examples/wizard.md) — multi-step form with per-step `trigger()`
  • [reference.md](reference.md) — decision trees, API tables, version-to-feature lookup

---

Which path applies

  • **A native `input`, `select` or `textarea`** — `register` hands the ref to the form, the field

stays uncontrolled, and typing re-renders nothing. Follow [examples/core.md](examples/core.md).

  • **A component that owns its value** — a custom select, date picker or rich text editor exposes no

usable ref, so `Controller` supplies `value` and `onChange` and confines the re-render to that field. Follow [examples/controlled-components.md](examples/controlled-components.md).

  • **Validation stated as a schema rather than as `register` rules** — pass it through `resolver` and

the field-level `rules` drop out. Follow [examples/validation.md](examples/validation.md).

---

<critical_requirements>

Before writing React Hook Form code

**Call `useForm<FormData>()` with a generic and with `defaultValues` for every field.** The generic types each field path and the submit payload; the defaults mount every input controlled from the first render.

**Key `useFieldArray` rows on `field.id`.** It is the identity RHF assigns the row and it survives add, remove and reorder, which an array index does not.

**Reach for `Controller` as soon as a component holds its own value.** `register` needs a ref that reaches a native input, and a component that does not forward one never joins the form.

**Set `mode` to `"onBlur"` or `"onTouched"`.** The default `"onSubmit"` withholds feedback until the first submit, and `"onChange"` validates on every keystroke.

**Pass schema validation through `resolver`, with the schema in its own module.** A schema outside the component is testable on its own and reusable across forms, and the form keeps only the wiring.

</critical_requirements>

---

**Auto-detection:** react-hook-form, useForm, register, handleSubmit, formState, Controller, useFieldArray, useWatch, useFormContext, useFormState, FormProvider, FormStateSubscribe, SubmitHandler, resolver, shouldUnregister, valueAsNumber

**Applies to:**

  • Form state, submission and validation wiring in React
  • Repeatable field groups that add, remove and reorder
  • Components that hold their own value and need bridging into the form
  • Multi-step flows where one form spans several screens
  • Narrowing re-renders in a form with many fields

**Handled elsewhere:**

  • Authoring the validation schema — `resolver` accepts a schema object and this skill only wires it

in; how the schema states its rules is settled by whatever owns it.

  • Where the initial values came from — the form receives them as a prop or through the `values`

option and fetches nothing itself.

  • Markup and styling of the inputs — every example uses plain elements, so the class names are yours.

---

<philosophy>

Form state lives outside React state. Inputs register themselves with the form and report through a ref, so a keystroke updates the form's own store without re-rendering the component that owns the field. Everything that reads form state — an error message, a computed total, a submit button — opts in by subscribing to a named slice, and a component that subscribes to nothing never re-renders.

This is why the wide reads cost so much. `watch()` in a render body and a `formState` destructure that pulls six properties both subscribe to the entire form, undoing the isolation the library was built for.

</philosophy>

---

<patterns>

Core patterns

Pattern 1: useForm with a generic

The generic, `mode` and `defaultValues` together settle type safety, validation timing and controlled-input warnings.

const {
  register,
  handleSubmit,
  formState: { errors, isSubmitting },
} = useForm<ContactFormData>({
  mode: "onBlur",
  defaultValues: { name: "", email: "", message: "" },
});

Full code: [examples/core.md](examples/core.md)

---

Pattern 2: Controller for components that own their value

`Controller` renders the field itself and hands it `value` and `onChange`. The test for which to reach for: a component whose `ref` forwards to a native input works with `register`, and anything else needs `Controller`.

<Controller
  name="service"
  control={control}
  rules={{ required: "Service is required" }}
  render={({ field, fieldState: { error } }) => (
    <>
      <Select {...field} options={serviceOptions} />
      {error && <span role="alert">{error.message}</span>}
    </>
  )}
/>

Full code: [examples/controlled-components.md](examples/controlled-com

Read more
Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin

Other skills on agents-inc-skills.