第185章:RHF と Zod を合体させる
この章でできるようになること 🧠🎯
- **Zodで書いたルール(スキーマ)**を、**React Hook Form(RHF)**にそのまま接続できるようになるよ ✅
- フォームの入力値が **「型」+「バリデーション」**で一気に安全になるよ 🛡️✨
- エラーメッセージも
errors.xxx?.messageでスッと表示できるようになるよ 🧸💬
まずは入れるもの(WindowsのターミナルでOK)💻📦
プロジェクトのルート(package.jsonがある場所)で👇
npm i react-hook-form zod @hookform/resolvers
@hookform/resolvers は Zod を RHF に繋ぐ“橋渡し”だよ 🌉✨ (npm)
全体の流れを図でイメージしよ 🗺️✨(Mermaid)
実装してみよう!🎮✨(最小で理解する版)
1) Zodで「入力ルール」を作る 🧩
たとえば「メール+パスワード」の超基本でいくね📧🔑 (ここでは**“合体”**が主役だから、ルールはシンプルでOK🙆♀️)
src/components/RhfZodDemo.tsx を作って👇
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
// ① Zodスキーマ(=入力ルール表)
const schema = z.object({
email: z.string().email("メールの形がちがうよ 📧"),
password: z.string().min(8, "パスワードは8文字以上だよ 🔑"),
});
// ② スキーマから型を自動生成(ここ超気持ちいい✨)
type FormValues = z.infer<typeof schema>;
export function RhfZodDemo() {
// ③ resolver に zodResolver(schema) を渡すだけ!
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<FormValues>({
resolver: zodResolver(schema),
mode: "onSubmit",
});
const onSubmit = async (data: FormValues) => {
// 送信中っぽく見せる(デモ用)
await new Promise((r) => setTimeout(r, 400));
alert(`OK! 🎉\nemail: ${data.email}`);
};
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate style={{ maxWidth: 420 }}>
<h2>RHF × Zod 合体デモ ✨</h2>
<div style={{ marginTop: 12 }}>
<label>
メール 📧
<input
type="email"
autoComplete="email"
{...register("email")}
style={{ display: "block", width: "100%", padding: 8, marginTop: 6 }}
/>
</label>
{errors.email?.message && (
<p style={{ margin: "6px 0 0" }}>{errors.email.message}</p>
)}
</div>
<div style={{ marginTop: 12 }}>
<label>
パスワード 🔑
<input
type="password"
autoComplete="current-password"
{...register("password")}
style={{ display: "block", width: "100%", padding: 8, marginTop: 6 }}
/>
</label>
{errors.password?.message && (
<p style={{ margin: "6px 0 0" }}>{errors.password.message}</p>
)}
</div>
<button
type="submit"
disabled={isSubmitting}
style={{ marginTop: 16, padding: "8px 12px" }}
>
{isSubmitting ? "送信中… 🌀" : "送信する 🚀"}
</button>
</form>
);
}
ポイントはこの3つだけだよ👇🥳
resolver: zodResolver(schema)を渡す (POCKETSIGN TECH BLOG)type FormValues = z.infer<typeof schema>で型が勝手にできる ✨errors.email?.messageを表示するだけでエラーUI完成 💬
※ z.string().email() のメール判定は Zodの標準だよ 📧 (Zod)
2) App.tsx に表示して動作確認 👀✨
src/App.tsx をこんな感じに👇
import { RhfZodDemo } from "./components/RhfZodDemo";
export default function App() {
return (
<div style={{ padding: 24 }}>
<RhfZodDemo />
</div>
);
}
起動して確認👇🚀
npm run dev
- 変なメール(
aaaとか)で送信 → エラー出る ⚠️ - パスワード7文字 → エラー出る ⚠️
- 正しく入れる →
alert出る 🎉
つまずきがちなところ(ここ大事)🧯💥
✅ zodResolver の import がコケる/バージョン相性が悪い
Zod と @hookform/resolvers の組み合わせで、バージョン差が原因のエラーが出ることがあるよ💦
(例:zod/v4/core が無い系) (GitHub)
困ったらまずこれでOK👇✨
npm i zod@latest @hookform/resolvers@latest
あと、Zod v4 対応は @hookform/resolvers を新しめ(v5.1.0以降)にするのが目安だよ🔧 (Zenn)
ミニ練習(30分で終わるやつ)⏱️🍓
schemaに次を追加してみて👇
passwordに「数字を1個以上」ルール(.regex(...))を足す 🔢
- 送信成功したら
alertじゃなくて画面に「ログイン成功🎉」って表示してみて✨ (useStateでOK!)
次の章につながる一言 📚➡️✨
この章で **「RHFにZodを合体させる配線」**は完璧〜!🎉 次の 第186章は、ここに 文字種チェック付きのログインフォームを作って「実戦」にするよ🔥😆