jpskill.com
💬 コミュニケーション コミュニティ

react-hook-form

Build performant forms in React with React Hook Form. Use when a user asks to handle form validation, build complex multi-step forms, integrate forms with Zod schemas, or reduce form re-renders in React.

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o react-hook-form.zip https://jpskill.com/download/15321.zip && unzip -o react-hook-form.zip && rm react-hook-form.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/15321.zip -OutFile "$d\react-hook-form.zip"; Expand-Archive "$d\react-hook-form.zip" -DestinationPath $d -Force; ri "$d\react-hook-form.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して react-hook-form.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → react-hook-form フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1
📖 Claude が読む原文 SKILL.md(中身を展開)

この本文は AI(Claude)が読むための原文(英語または中国語)です。日本語訳は順次追加中。

React Hook Form

Overview

React Hook Form is a performant form library that minimizes re-renders. Unlike controlled components (which re-render on every keystroke), RHF uses uncontrolled inputs and only re-renders when necessary. Integrates with Zod for schema validation.

Instructions

Step 1: Basic Form with Zod

npm install react-hook-form @hookform/resolvers zod
// components/SignupForm.tsx — Form with Zod validation
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const signupSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email address'),
  password: z.string()
    .min(8, 'Password must be at least 8 characters')
    .regex(/[A-Z]/, 'Must contain an uppercase letter')
    .regex(/[0-9]/, 'Must contain a number'),
  confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ['confirmPassword'],
})

type SignupInput = z.infer<typeof signupSchema>

export function SignupForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<SignupInput>({
    resolver: zodResolver(signupSchema),
  })

  const onSubmit = async (data: SignupInput) => {
    const res = await fetch('/api/auth/signup', {
      method: 'POST',
      body: JSON.stringify(data),
    })
    if (!res.ok) throw new Error('Signup failed')
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="name">Name</label>
        <input {...register('name')} />
        {errors.name && <p className="error">{errors.name.message}</p>}
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <input type="email" {...register('email')} />
        {errors.email && <p className="error">{errors.email.message}</p>}
      </div>

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

      <div>
        <label htmlFor="confirmPassword">Confirm Password</label>
        <input type="password" {...register('confirmPassword')} />
        {errors.confirmPassword && <p className="error">{errors.confirmPassword.message}</p>}
      </div>

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Creating account...' : 'Sign Up'}
      </button>
    </form>
  )
}

Step 2: Dynamic Fields

// components/InvoiceForm.tsx — Dynamic line items
import { useForm, useFieldArray } from 'react-hook-form'

interface InvoiceForm {
  clientName: string
  items: Array<{ description: string; quantity: number; price: number }>
}

export function InvoiceForm() {
  const { register, control, handleSubmit, watch } = useForm<InvoiceForm>({
    defaultValues: { items: [{ description: '', quantity: 1, price: 0 }] },
  })

  const { fields, append, remove } = useFieldArray({ control, name: 'items' })
  const items = watch('items')
  const total = items.reduce((sum, item) => sum + item.quantity * item.price, 0)

  return (
    <form onSubmit={handleSubmit(console.log)}>
      <input {...register('clientName')} placeholder="Client name" />

      {fields.map((field, index) => (
        <div key={field.id} className="flex gap-2">
          <input {...register(`items.${index}.description`)} placeholder="Description" />
          <input type="number" {...register(`items.${index}.quantity`, { valueAsNumber: true })} />
          <input type="number" {...register(`items.${index}.price`, { valueAsNumber: true })} step="0.01" />
          <button type="button" onClick={() => remove(index)}>×</button>
        </div>
      ))}

      <button type="button" onClick={() => append({ description: '', quantity: 1, price: 0 })}>
        Add Item
      </button>

      <p>Total: ${total.toFixed(2)}</p>
      <button type="submit">Create Invoice</button>
    </form>
  )
}

Step 3: Server Actions (Next.js)

// app/settings/page.tsx — Server action with RHF
'use client'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { updateProfile } from './actions'

export function ProfileForm({ user }) {
  const form = useForm({
    resolver: zodResolver(profileSchema),
    defaultValues: { name: user.name, bio: user.bio },
  })

  return (
    <form action={async (formData) => {
      const valid = await form.trigger()
      if (!valid) return
      await updateProfile(formData)
    }}>
      <input {...form.register('name')} />
      <textarea {...form.register('bio')} />
      <button type="submit">Save</button>
    </form>
  )
}

Guidelines

  • Always use Zod resolver — share validation between frontend forms and API routes.
  • Use useFieldArray for dynamic lists (invoice items, team members, addresses).
  • register uses uncontrolled inputs — fastest performance, minimal re-renders.
  • Use watch sparingly — it triggers re-renders. Use useWatch for isolated subscriptions.
  • For complex forms with many sections, use FormProvider to pass form context without prop drilling.