Skip to content
Development
Skill

/react-hook-form-zod

Type-safe React forms with React Hook Form and Zod validation. Use for form schemas, field arrays, multi-step forms, or encountering validation errors, resolver issues, nested field problems.

From plugin
secondsky-claude-skills
219183 skills42 agents62 commands2 MCP
Install
$ npx -y skills add secondsky/claude-skills --skill react-hook-form-zod --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/react-hook-form-zod

Context preview

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

Type-safe React forms with React Hook Form and Zod validation. Use for form schemas, field arrays, multi-step forms, or encountering validation errors, resolver issues, nested field problems.

SKILL.md

react-hook-form-zod.SKILL.md
name: react-hook-form-zod
description: "Type-safe React forms with React Hook Form and Zod validation. Use for form schemas, field arrays, multi-step forms, or encountering validation errors, resolver issues, nested field problems."


metadata:
  keywords:
    - react-hook-form
    - useForm
    - zod validation
    - zodResolver
    - "@hookform/resolvers"
    - form schema
    - register
    - handleSubmit
    - formState
    - useFieldArray
    - useWatch
    - useController
    - Controller
    - shadcn form
    - Field component
    - client server validation
    - nested validation
    - array field validation
    - dynamic fields
    - multi-step form
    - async validation
    - zod refine
    - z.infer
    - form error handling
    - uncontrolled to controlled
    - resolver not found
    - schema validation error

license: MIT

React Hook Form + Zod Validation

**Status**: Production Ready ✅ **Last Updated**: 2025-11-21 **Dependencies**: None (standalone) **Latest Versions**: react-hook-form@7.84.0, zod@4.3.6, @hookform/resolvers@5.2.2

---

Quick Start (10 Minutes)

1. Install Packages

bun add react-hook-form@7.84.0 zod@4.3.6 @hookform/resolvers@5.2.2

**Why These Packages**:

  • **react-hook-form**: Performant, flexible forms with minimal re-renders
  • **zod**: TypeScript-first schema validation with type inference
  • **@hookform/resolvers**: Adapter connecting Zod to React Hook Form

2. Create Your First Form

import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

// 1. Define validation schema
const loginSchema = z.object({
  email: z.email({ error: 'Invalid email address' }),
  password: z.string().min(8, 'Password must be at least 8 characters'),
})

// 2. Infer TypeScript type from schema
type LoginFormData = z.infer<typeof loginSchema>

function LoginForm() {
  // 3. Initialize form with zodResolver
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<LoginFormData>({
    resolver: zodResolver(loginSchema),
    defaultValues: {
      email: '',
      password: '',
    },
  })

  // 4. Handle form submission
  const onSubmit = async (data: LoginFormData) => {
    // Data is guaranteed to be valid here
    console.log('Valid data:', data)
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" type="email" {...register('email')} />
        {errors.email && (
          <span role="alert" className="error">
            {errors.email.message}
          </span>
        )}
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <input id="password" type="password" {...register('password')} />
        {errors.password && (
          <span role="alert" className="error">
            {errors.password.message}
          </span>
        )}
      </div>

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

**CRITICAL**:

  • Always set `defaultValues` to prevent "uncontrolled to controlled" warnings
  • Use `zodResolver(schema)` to connect Zod validation
  • Type form with `z.infer<typeof schema>` for full type safety
  • Validate on both client AND server (never trust client validation alone)

**Template**: See `templates/basic-form.tsx` for complete working example

3. Add Server-Side Validation

// server/api/login.ts
import { z } from 'zod'

// SAME schema on server
const loginSchema = z.object({
  email: z.email({ error: 'Invalid email address' }),
  password: z.string().min(8, 'Password must be at least 8 characters'),
})

export async function loginHandler(req: Request) {
  try {
    const data = loginSchema.parse(await req.json())
    // Data is type-safe and validated
    return { success: true }
  } catch (error) {
    if (error instanceof z.ZodError) {
      return { success: false, errors: z.flattenError(error).fieldErrors }
    }
    throw error
  }
}

**Why Server Validation**:

  • Client validation can be bypassed (inspect element, Postman, curl)
  • Server validation is your security layer
  • Same Zod schema = single source of truth

**Template**: See `templates/server-validation.tsx`

---

Core Concepts

useForm Hook

const {
  register,           // Register input fields
  handleSubmit,       // Wrap onSubmit handler
  formState,          // Form state (errors, isValid, isDirty, etc.)
  setValue,           // Set field value programmatically
  getValues,          // Get current form values
  watch,              // Watch field values
  reset,              // Reset form to defaults
  trigger,            // Trigger validation manually
  control,            // For Controller/useController
} = useForm<FormData>({
  resolver: zodResolver(schema),
  mode: 'onSubmit',               // When to validate
  defaultValues: {},              // Initial values (REQUIRED)
})

**Validation Modes**:

  • `onSubmit` - Validate on submit (best performance)
  • `onChange` - Validate on every change (live feedback)
  • `onBlur` - Validate when field loses focus (good balance)
  • `all` - Validate on submit, blur, and change

**Reference**: See `references/rhf-api-reference.md` for complete API

Zod Schema Basics

import { z } from 'zod'

// Basic types
const schema = z.object({
  email: z.email({ error: 'Invalid email' }),
  age: z.number().min(18, 'Must be 18+'),
  terms: z.boolean().refine(val => val === true, 'Must accept terms'),
})

// Nested objects
const addressSchema = z.object({
  user: z.object({
    name: z.string(),
    email: z.email(),
  }),
  address: z.object({
    street: z.string(),
    city: z.string(),
    zip: z.string().regex(/^\d{5}$/),
  }),
})

// Arrays
const tagsSchema = z.object({
  tags: z.array(z.string()).min(1, 'At least one tag required'),
})

// Optional and n
Read more
Ships withsecondsky-claude-skills

145 production-ready skills for Claude Code CLI 🔌 Platform / Harness Support These plugins ship as Claude Code marketplace plugins (.claude-plugin/ manifests) and Codex CLI plugins (.codex-plugin/ manifests).

Get the whole plugin

Other skills on secondsky-claude-skills.