Next.js × react-hook-form × zod × Resend で作るお問い合わせフォーム開発

Next.js × react-hook-form × zod × Resend で作るお問い合わせフォーム開発

8/16/2024著者:ShinCode

どうも、Shinです。

お問い合わせフォームはほとんどWebサイトやアプリに導入されると思います。なのでモダンな技術スタックを使ってお問い合わせフォームを型定義とバリデーションチェックを正常に行って開発する流れを記録として残します。

ちなみにこの記事の内容は動画でも取り扱っているので、動画で学びたいとは下記をチェックしてみてください。

完成品デモ

👇の画像が完成品のお問い合わせフォームです。
(画像)
よくあるフォームだと思います。ファイル添付まで実装しています。使う技術スタックは

  • Next.js(React)
  • react-hook-form
  • zod
  • Resend(メール送信サービス)

です。

SendGridの方がResendよりも無料枠では多く使えます。ただ有料になるとResendの方が安く運用できるのでResendを採用しました。AWS SESでも良いと思います。

まずは必要なライブラリのインストールからはじめます。

!

すでにNext.jsプロジェクトの雛形を準備した状態で行ってください。

必要なライブラリとUIコンポーネントの導入

以下のライブラリを入れます。

npm install react-hook-form zod resend

です。zodは型定義ができるのとバリデーションチェックに最適なライブラリです。

次に、shadcn/uiと呼ばれるUIコンポーネントをの初期化を行います。Next.jsプロジェクトのルートディレクトリで行います。

npx shadcn-ui@latest init

これで初期化がはじまります。出てくる質問には全てYesで答えていきましょう。

続いて、必要なUIコンポーネントを導入します。今回はフォーム、ボタン、インプット、テキストエリアに関するコンポーネントを持ってきます。

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

これだけで簡単に/components/ui/xxx(form, button, input, textarea).tsxが作成されます。このコンポーネントをあとは必要な時にimportしてくるだけでOK。めっちゃ便利です。

お問い合わせフォームコンポーネントの作成

次にお問い合わせフォームコンポーネントを作成します。

app/components/MailForm.tsx

"use client";

import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { formSchema } from "@/lib/formShema";
import { zodResolver } from "@hookform/resolvers/zod";
import React from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { Input } from "./ui/input";
import { Button } from "./ui/button";
import { Textarea } from "./ui/textarea";
import useMailForm from "@/app/hooks/useMailForm";

const MailForm = () => {
  const { form, onSubmit } = useMailForm();

  return (
    <Form {...form}>
      <form
        onSubmit={form.handleSubmit(onSubmit)}
        className="flex flex-col gap-3 container"
      >
        <FormField
          control={form.control}
          name="username"
          render={({ field }) => (
            <FormItem>
              <FormLabel>お名前</FormLabel>
              <FormControl>
                <Input {...field} placeholder="お名前" />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>メールアドレス</FormLabel>
              <FormControl>
                <Input {...field} placeholder="メールアドレス" />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="content"
          render={({ field }) => (
            <FormItem>
              <FormLabel>本文</FormLabel>
              <FormControl>
                <Textarea
                  placeholder="本文"
                  className="resize-none"
                  {...field}
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="file"
          render={({ field: { value, onChange, ...fieldProps } }) => (
            <FormItem>
              <FormLabel>Profile Picture</FormLabel>
              <FormControl>
                <Input
                  type="file"
                  {...fieldProps}
                  accept="image/*"
                  onChange={(event) => {
                    onChange(event.target.files && event.target.files);
                  }}
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit" className="mt-3" style={{ width: "100%" }}>
          送信
        </Button>
      </form>
    </Form>
  );
};

export default MailForm;

いきなりたくさん記述しましたが、大丈夫です。まずはuseMailForm()から作成していきましょう。これはコード量を減らすためのカスタムフックスです。

\ SNSでシェアしよう /
記事を共有する