react-hook-form + Zod でフォームバリデーション入門
react-hook-form と zod を組み合わせると、フォームのバリデーションとエラーメッセージ表示を型安全に実装できます。zodResolver がスキーマとフォームをつなぐ役割を担います。
早わかりまとめ
| 役割 | パッケージ |
|---|---|
| フォーム管理 | react-hook-form |
| スキーマ定義・型生成 | zod |
| 橋渡し | @hookform/resolvers/zod(zodResolver) |
インストール
npm install react-hook-form zod @hookform/resolvers
基本パターン
import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' // ① スキーマ定義 const schema = z.object({ email: z.string().email({ message: 'メールアドレスを入力してください' }), password: z.string().min(8, { message: '8文字以上で入力してください' }), }) // ② スキーマから型を生成 type FormValues = z.infer<typeof schema> const SignInForm = () => { const { register, handleSubmit, formState: { errors, isValid }, } = useForm<FormValues>({ // ③ zodResolver でスキーマを渡す resolver: zodResolver(schema), mode: 'onChange', // 入力のたびにバリデーション }) const onSubmit = (data: FormValues) => { // data は schema を通過済みなので型安全 console.log(data) } return ( <form onSubmit={handleSubmit(onSubmit)}> <div> <input type="email" placeholder="Email" {...register('email')} /> {errors.email && <p>{errors.email.message}</p>} </div> <div> <input type="password" placeholder="Password" {...register('password')} /> {errors.password && <p>{errors.password.message}</p>} </div> <button type="submit" disabled={!isValid}>ログイン</button> </form> ) }
mode の違い
useForm({ mode: 'onChange' }) // 入力のたびにバリデーション
useForm({ mode: 'onBlur' }) // フォーカスが外れたときにバリデーション
useForm({ mode: 'onSubmit' }) // submit 時にのみバリデーション(デフォルト)
複雑なスキーマ例
const profileSchema = z.object({
name: z.string().min(1, { message: '名前を入力してください' }),
age: z.number({ invalid_type_error: '数値を入力してください' }).min(0).max(150),
website: z.string().url({ message: 'URLの形式で入力してください' }).optional(),
role: z.enum(['admin', 'user', 'guest']),
})
type ProfileValues = z.infer<typeof profileSchema>
defaultValues でデフォルト値を設定する
useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: {
email: '',
password: '',
},
})
watch でリアルタイムに値を取得する
const { register, watch } = useForm<FormValues>({ resolver: zodResolver(schema) }) const emailValue = watch('email') // emailValue が変わるたびに再レンダリング
reset でフォームをリセットする
const { reset, handleSubmit } = useForm<FormValues>({ resolver: zodResolver(schema) }) const onSubmit = (data: FormValues) => { // 送信後にリセット reset() }
まとめ
| やること | コード |
|---|---|
| スキーマ定義 | z.object({ ... }) |
| 型生成 | type T = z.infer<typeof schema> |
| フォームに組み込む | resolver: zodResolver(schema) |
| エラー表示 | errors.フィールド名?.message |
| 送信可否の制御 | disabled={!isValid} |
| バリデーションタイミング | mode: 'onChange' など |
Zod の基本的なスキーマ定義については「Zod 入門:z.object・z.infer・safeParse の使い方」を参照してください。