😏
shadcn/uiを使用したフォーム作成(Next.js,zod,react-hook-form)
はじめに
先日shadcn/uiを初めて触ったので、shadcn/uiの勉強がてらzodとreact-hook-formを使用してフォームを作成したので自分のための忘備録となります。
もしご興味がある方は先日の私の記事をご覧ください。
GitHub
環境構築
プロジェクト作成
npx create-next-app@latest
shadcn/uiの初期化
npx shadcn@latest init
formに関するコンポーネントインストール
npx shadcn@latest add form
inputコンポーネントインストール
npx shadcn@latest add input
checkboxコンポーネントインストール
npx shadcn@latest add checkbox
完成時ディレクトリ
├── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── components
│ ├── contact
│ │ ├── ContactPage.tsx
│ │ └── schema.ts
│ └── ui
│ ├── button.tsx
│ ├── checkbox.tsx
│ ├── form.tsx
│ ├── input.tsx
│ ├── label.tsx
│ └── textarea.tsx
└── lib
└── utils.ts
フォーム作成
page.tsx
import ContactPage from "@/components/contact/ContactPage";
export default function Home() {
return (
<main>
<ContactPage />
</main>
);
}
schema.ts
import { z } from "zod";
const name: z.ZodString = z
.string({ required_error: "必須項目です" })
.min(1, { message: "必須項目です" })
.max(30, { message: "入力値が長すぎます" });
const email: z.ZodString = z
.string({ required_error: "必須項目です" })
.min(1, { message: "必須項目です" })
.max(50, { message: "入力値が長すぎます" })
.email({ message: "メールアドレスの形式で入力してください" });
const phone: z.ZodString = z
.string({ required_error: "必須項目です" })
.min(10, { message: "電話番号を入力してください" })
.max(15, { message: "入力値が長すぎます" });
const message: z.ZodString = z
.string({ required_error: "必須項目です" })
.min(1, { message: "必須項目です" })
.max(5000, { message: "入力値が長すぎます" });
const agree: z.ZodLiteral<boolean> = z.literal(true, {
errorMap: () => ({ message: "同意が必要です" }),
});
export const ContactSchema = z.object({
name,
email,
phone,
message,
agree,
});
export type ContactType = z.infer<typeof ContactSchema>;
ContactPage.tsx
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { ContactSchema, ContactType } from "./schema";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "../ui/form";
import { Input } from "../ui/input";
import { Textarea } from "../ui/textarea";
import { Checkbox } from "../ui/checkbox";
import { Button } from "../ui/button";
const ContactPage = () => {
const form = useForm<ContactType>({
mode: "onSubmit",
resolver: zodResolver(ContactSchema),
defaultValues: {
name: "",
email: "",
phone: "",
message: "",
agree: false,
},
});
const handleOnSubmit = form.handleSubmit((data) => console.log(data));
return (
<div className="container h-screen mx-auto pt-10">
<h1 className="text-center font-bold text-2xl mb-10">お問い合わせ</h1>
<Form {...form}>
<form
method="post"
onSubmit={handleOnSubmit}
className="flex flex-col space-y-5"
>
{/* name */}
<FormField
control={form.control}
name={"name"}
render={({ field }) => (
<FormItem>
<FormLabel>お名前</FormLabel>
<FormControl>
<Input {...field} placeholder="例) 田中 太郎"></Input>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* email */}
<FormField
control={form.control}
name={"email"}
render={({ field }) => (
<FormItem>
<FormLabel>メールアドレス</FormLabel>
<FormControl>
<Input {...field} placeholder="例) test@email.com"></Input>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* phone */}
<FormField
control={form.control}
name={"phone"}
render={({ field }) => (
<FormItem>
<FormLabel>電話番号</FormLabel>
<FormControl>
<Input {...field} placeholder="例) 00011112222"></Input>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* message */}
<FormField
control={form.control}
name={"message"}
render={({ field }) => (
<FormItem>
<FormLabel>お問い合わせ内容</FormLabel>
<FormControl>
<Textarea
{...field}
placeholder="例) お問い合わせ内容"
className="resize-none h-50"
></Textarea>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* agree */}
<div className="self-center">
<FormField
control={form.control}
name={"agree"}
render={({ field }) => (
<FormItem>
<div className="flex mt-10">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel className="ml-2">個人情報取り扱いに同意する</FormLabel>
</div>
<FormMessage />
</FormItem>
)}
/>
</div>
<Button type="submit" className="cursor-pointer self-center mt-10">
送信する
</Button>
</form>
</Form>
</div>
);
};
export default ContactPage;
フォーム完成
全ての要素を必須項目にしているのでsubmitできない。
コンソールを見ると要素が取得できていることがわかる。
まとめ
独自のコンポーネント(FormFiled, FormItem, FormControl, FormLabel, FormMessage)とcheckboxのoncheckedChangeに戸惑ったが、理解してしまえばとても簡単にフォームを作成することができた。よりshadcn/uiが好きになった。
Discussion