Next.jsだけでお問い合わせフォームを作る方法を解説

blog-hero-imgNext.jsをフロントエンドとバックエンドに使ってお問い合わせ機能を作ります。AppフォルダとPagesフォルダ、両方のコードを紹介

pen-icon2023.12.23rewrite-icon2024.1.26

Profile Pic

この記事の筆者:三好アキ(エンジニア)


ウェブデザイナーから『エンジニア』『プログラマー』へ成長したい人、独学で進んでいきたい人を応援しています。 HTMLとCSSの知識だけでアプリ開発を始められる入門書を多数執筆中📕📗👇


ウェブ制作の教本『はじめてつくるReactアプリ』など複数冊を執筆。



React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて配信中。登録はこちらから → 無料メルマガ登録

作業の流れ

この記事は以下の流れで進みます。


1 ― Next.jsをインストール

2 ― フロントエンドに「お問い合わせページ」を作成

3 ― バックエンドのapiフォルダに、「お問い合わせページ」からPOSTリクエストを受ける機能を作成


お問合せがあったことをメールで知るために、Nodemailerというパッケージを使用します。

(*本記事ではNext.jsバージョン13で導入されたAppフォルダを使って解説を進めますが、以前使われていたPagesフォルダでも同じ流れでお問合せ機能を開発できるので、その互換性を重視して、サーバーコンポーネントとServer Actionsは使いません。)

Next.jsのインストール

まずNext.jsの新しいプロジェクトを用意しましょう。

npx create-next-app example-site

インストール時に出てくる質問については、下から2つ目のAppフォルダに関するもの以外はすべてNoを選びます。


✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? › No / Yes

Next.jsをVSコードで開きましょう。

本記事ではCSSのスタイル関係には触れませんが、globals.cssのコードがやや邪魔になるので、globals.cssのコードはすべて消しておいてください。

image

お問い合わせページ作り(フロントエンド)

フロントエンド側にお問い合わせページを作りましょう。

appフォルダ内にcontactフォルダを作り、その中にpage.jsファイルを作ってください。

image

そこに次のコードを打ちます。なおPagesフォルダを使っている場合は、1行目のuse clientは不要です。

// app/contact/page.js

"use client"

const Contact = () => {
    return (
        <div>
            <h1>コンタクト</h1>
            <form>
                <input type="text" placeholder="お名前" required/>
                <input type="text" placeholder="メールアドレス" required/>
                <textarea type="text" placeholder="メッセージ" rows="10" required></textarea>
                <button type="submit">送信</button>
            </form> 
        </div>
    )
}

export default Contact

保存してnpm run devでNext.jsを起動し、http://localhost:3000/contactを開くと、次のように表示されます。

image

次は、データをバックエンドに投稿するコードを追加しましょう。現時点ではまだバックエンドがないので、fetch()のURLはxxxとしています。

// app/contact/page.js

"use client"
import { useState } from "react" 

const Contact = () => {
    const [name, setName] = useState("")
    const [email, setEmail] = useState("")
    const [message, setMessage] = useState("")

    const handleSubmit = async(e) => {
        e.preventDefault()  
        try{
            const response = await fetch("xxx", {
                method: "POST",
                headers: { 
                    "Accept": "application/json", 
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({
                    name: name,
                    email: email,
                    message: message
                })
            })
            const jsonData = await response.json()
            alert("メッセージを送信しました")
        }catch(err){
            alert("メッセージの送信が失敗しました")
        }
    }

    return (
        <div>
            <h1>コンタクト</h1>
            <form onSubmit={handleSubmit}>
                <input value={name} onChange={(e) => setName(e.target.value)} type="text" placeholder="お名前" required/>
                <input value={email} onChange={(e) => setEmail(e.target.value)} type="text" placeholder="メールアドレス" required/>
                <textarea value={message} onChange={(e) => setMessage(e.target.value)} type="text" placeholder="メッセージ" rows="10" required></textarea>
                <button type="submit">送信</button>
            </form> 
        </div>
    )
}

export default Contact

ユーザーがメッセージを入力するフロントエンド側のページができたので、次はここから問い合わせを受け付けるバックエンド機能を作ります。

問い合わせを受け付ける機能の開発(バックエンド)

appフォルダ内にapiフォルダを作り、その中にフォルダcontact-handler、さらにその中にファイルroute.jsを作りましょう。

image

ここにフロントエンドからのデータ(=メッセージ)を受け付けるコードを書きますが、その前にまず必要なパッケージをインストールしましょう。

npm install nodemailer

そこに次のコードを打ちます。

注意してもらいたいのはtransporterに入力するSMTP関連のデータで、これはOutlookやZohoなど各自のメールアカウントから持ってきてください(OutlookのSMTPデータ)。なおGmailは、Vercelにデプロイしたあと、意図通りに動かないことがあります。

// app/api/contact-handler/route.js

import { NextResponse } from "next/server"
import nodeMailer from "nodemailer"

export async function POST(request) {
    const reqBody = await request.json()
    const { email, name, message } = reqBody

    try{
        const transporter = nodeMailer.createTransport({
            host: "smtp-mail.outlook.com",               // メールサーバー。ここではHotmail/Outlookを使った例
            port: 587,
            secure: false,
            auth: {
                user: "my-email@hotmail.co.jp",           // メールアドレス
                pass: "my-password"                       // パスワード
            }
        })
    
        const mailOptions = {
            from: "My website",
            to: "receive-email@gmail.com",
            subject: "Next.jsコンタクトページ",
            text: `名前: ${name} \n\nメールアドレス: ${email} \n\nメッセージ: ${message}`
        }
    
        const info = await transporter.sendMail(mailOptions)
        return NextResponse.json({message: "成功しました"})
    }catch(err){
        return NextResponse.json({message: "失敗しました"})
    }
}

もしPagesフォルダを使っている場合は、pagesフォルダ内にapiフォルダ、その中にcontact-handler.jsファイルを作り、次のコードを打ちます。

// pages/api/contact-handler.js

import nodeMailer from "nodemailer"

export default function contactHandler(req, res){
    if (req.method === "POST") {
        const { email, name, message } = req.body

        const transporter = nodeMailer.createTransport({
            host: "smtp-mail.outlook.com",         // メールサーバー。ここではHotmail/Outlookを使った例
            port: 587,
            secure: false,
            auth: {
                user: "my-email@hotmail.co.jp",           // メールアドレス
                pass: "my-password"                       // パスワード
            }
        })
    
        const mailOptions = {
            from: "My website",
            to: "receive-email@gmail.com",
            subject: "Next.jsコンタクトページ",
            text: `名前: ${name} \n\nメールアドレス: ${email} \n\nメッセージ: ${message}`
        }

        transporter.sendMail(mailOptions, (err, info) => {
            if(err){
                return res.json({message: "失敗しました"})
            }else{
                return res.json({message: "成功しました"})
            }
        })
    }
}

これでバックエンド側の機能は完成です。

最後にフロントエンド側のfetch()のURLを修正しましょう。

// app/contact/page.js

const response = await fetch("http://localhost:3000/api/contact-handler", {....

なおデプロイする場合には、http://localhost:3000をデプロイ後の公開URLに変更することを忘れないようにしましょう。