Build type-safe validated forms in React using React Hook Form and Zod. Single schema can validate on client and server with TypeScript inference via z.infer.
Use when building forms with validation, integrating project Field/Input patterns, multi-step wizards, useFieldArray, fixing uncontrolled/controlled warnings, resolver errors, or async validation.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Build type-safe validated forms in React using React Hook Form and Zod. Single schema can validate on client and server with TypeScript inference via z.infer.
Use when building forms with validation, integrating project Field/Input patterns, multi-step wizards, useFieldArray, fixing uncontrolled/controlled warnings, resolver errors, or async validation.
user-invocable
false
allowed-tools
Bash(npm *), Bash(pnpm *), Bash(bun *)
React Hook Form + Zod
Suggested versions (verify against your lockfile): react-hook-form, zod, @hookform/resolvers.
Register with contacts.${index}.name (or equivalent) and read nested errors with optional chaining.
Server validation
Reuse the same schema (or a stricter server variant) on the API. Client validation is not a security boundary.
Map API field errors with setError from useForm.
UI in this repo
Prefer project Field, Input, and related primitives from @/shared/components/ui per existing patterns. Wire them with Controller or register depending on whether the component forwards a ref.
Performance
Avoid watch() without arguments; prefer watch("fieldName") when you need one value.
Prefer register over Controller for plain inputs.
Rules of thumb
Always provide defaultValues for fields you register (avoids uncontrolled → controlled warnings).
Never skip server-side validation for persisted or privileged actions.
One primary resolver (zodResolver); merge Zod schemas instead of stacking competing resolvers.
Do not use array index as React key for useFieldArray rows.