🗂

初学者必見!shadcn/ui × React Hook Form × Zod で型安全な美しいフォームを作成!!!

に公開

💡 概要

本記事では、以下の技術スタックを用いて、美しくかつtypeScriptの型安全なフォームを構築する方法について紹介します。

主に、フォームのバリデーションUIの一貫性確保保守性の高い実装を意識しました。


🧰 技術スタック

ライブラリ 用途
shadcn/ui コンポーネントUIライブラリ(Tailwindベース)
react-hook-form フォームの状態管理
zod スキーマベースのバリデーション
TypeScript 静的型チェックと言語サポート

🔧 セットアップ

shadcn/ui のインストール

npx shadcn-ui@latest init

フォーム用ライブラリの導入

npm install react-hook-form zod @hookform/resolvers

1. react-hook-form

  • React でフォームを扱うためのライブラリ。
  • 軽量・高速で、制御が明確。
  • フォーム状態管理(値・エラー・バリデーション)を簡潔に扱える。

主な特徴

  • フックベース:useForm, Controller, handleSubmit など
  • パフォーマンス最適化済み(再レンダリングが最小限)
  • シンプルなAPIで状態管理ができる

2. zod

  • バリデーションと型定義を同時に行える スキーマベースのバリデーションライブラリ。
  • Yup や Joi の代替として人気だが、Zodは TypeScriptとの親和性が圧倒的に高い。

const schema = z.object({
  email: z.string().email(),
  age: z.number().min(18),
});
  • z.infer<typeof schema> を使えば、Zodのスキーマからそのまま型を生成できます!

3. @hookform/resolvers

  • react-hook-form と zod, yup, joi などの バリデーションライブラリを接続するための橋渡しライブラリ。

  • これがあることで、Zod で書いたスキーマを React Hook Form に統合できる。

import { zodResolver } from "@hookform/resolvers/zod";

const form = useForm<FormData>({
  resolver: zodResolver(schema), // ← ZodとReact Hook Formを接続
});

shadcn/uiより必要なものをインストール

今回はInput,Button,Formを利用します。そのために以下でインストールします。
※本ページの説明はnpmを利用しています。

npx shadcn-ui@latest add input
npx shadcn-ui@latest add button
npx shadcn-ui@latest add form

今回はnameとemailだけですが、文章入力したい場合はtextareaをインストールしてください!

ユーザー登録フォーム

実際の実装例を紹介します!

// components/UserForm.tsx

import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";

// Zodスキーマ定義
const schema = z.object({
  name: z.string().min(1, "名前は必須です"),
  email: z.string().email("有効なメールアドレスを入力してください"),
});

type FormData = z.infer<typeof schema>;

export function UserForm() {
  const form = useForm<FormData>({
    resolver: zodResolver(schema),
    defaultValues: { name: "", email: "" },
  });

  const onSubmit = (data: FormData) => {
    console.log("送信データ:", data);
  };

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormField
          control={form.control}
          name="name"
          render={({ field }) => (
            <FormItem>
              <FormLabel>名前</FormLabel>
              <FormControl>
                <Input placeholder="山田 太郎" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>メールアドレス</FormLabel>
              <FormControl>
                <Input placeholder="example@mail.com" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <Button type="submit">送信</Button>
      </form>
    </Form>
  );
}

このコードはnameとemailのフォームを実装しています。
nameは1文字以上必須、emailはmail型でないと認識しません。

送信ボタンを押すことでコンソール上で入力したフォームのデータが表示されるようになります。

🧠 実装のポイント

zod による型定義はそのままフォームバリデーションに利用可能!

shadcn/ui は Tailwind を活用して、UIの一貫性と柔軟なカスタマイズが可能!

react-hook-form の useForm によりフォームステートの管理が簡単!

FormField や FormMessage などのラップコンポーネントで、コードの再利用性UI!

✅ まとめ

shadcn/ui × react-hook-form × zod の組み合わせにより、

UIとUXの両立

型安全なバリデーション

メンテナンスしやすいコード構造

を実現できます。プロジェクトの初期設計やチーム開発においてもおすすめのスタックです!

Discussion