Nodemailer Setup
Used in this guide:Reason:
Implement a secure email-sending functionality using Nodemailer and Gmail SMTP for contact forms.
Setup Google SMTP
First, go to Google Account Security and enable 2-steps verification. Once that's done, type “app password” into the search bar at the top and select "App passwords (Security)" from the results. Enter a name for your app and click Create. Google will generate a 16-character password that you'll need to set up Nodemailer. Make sure to save this password somewhere safe, as it won’t be shown again.
Install Nodemailer
To use Nodemailer in your Next.js project, install it along with its type definitions:
npm install nodemailernpm install @types/nodemailerPrepare environment variables
Create a file called .env.local in the root directory and enter your email and Google App Password accordingly:
# put your app password from Google Security -> App Password here
SMTP_PASSWORD=your_google_app_password
# put your email here (same email used to setup App Password)
SMTP_EMAIL=your_email
# don't change anything here
SMTP_HOST=smtp.gmail.comCreate API route
This file sets up a POST API route to handle contact form submissions using Nodemailer. It reads the user's name, email, and message from the request body, then sends an email via Gmail SMTP using environment variables that we created in the previous step for authentication.
import { NextResponse } from "next/server";
import nodemailer from "nodemailer";
export async function POST(request: Request) {
const { name, email, message } = await request.json();
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
secure: false,
auth: {
user: process.env.SMTP_EMAIL,
pass: process.env.SMTP_PASSWORD,
}
});
try {
const info = await transporter.sendMail({
from: `${name}: <${email}>`,
to: process.env.SMTP_EMAIL,
subject: "Email from my website",
text: `You've received a new message from ${name} (${email}):\n\n${message}`,
});
console.log("Email sent: %s", info.messageId);
return NextResponse.json({ message: "Email sent successfully" });
} catch (error) {
console.error("Error sending email: ", error);
return NextResponse.json({ message: "Error sending email."}, { status: 500 });
}
}Create a form component
This component renders a contact form and sends user input (name, email, message) to the /api/util/send-email route on submit. It uses fetch to POST the form data as JSON and displays a status message based on the response. Input fields are controlled with React useState, and basic styling is applied using Tailwind utility classes.
'use client'
import { useState } from "react";
const SendEmailForm = () => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const [status, setStatus] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setStatus("Sending the email...");
try {
const res = await fetch("/api/util/send-email", {
method: 'POST',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name, email, message }),
});
if (res.ok) {
setStatus("Email sent successfully");
setName("");
setEmail("");
setMessage("");
} else {
setStatus("Failed to send email, please try again.");
}
} catch (error) {
console.error("Error sending email: ", error);
setStatus("Failed to send email, please try again.");
}
};
return (
<form onSubmit={handleSubmit} className="w-[600px] flex flex-col gap-[24px] p-[24px] bg-gray-600">
<div className="flex gap-[24px] w-full">
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
required
className="bg-gray-500 p-[8px] w-full max-w-[50%]"
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="bg-gray-500 p-[8px] w-full max-w-[50%]"
/>
</div>
<textarea
placeholder="Message.."
value={message}
onChange={(e) => setMessage(e.target.value)}
required
className="bg-gray-500 p-[8px] w-full min-h-[200px]"
/>
{ status && (
<div className="w-full text-violet-300">{status}</div>
)}
<button
type="submit"
className="bg-violet-300 py-[6px] px-[24px]"
>Send</button>
</form>
)
}
export default SendEmailForm;Use the form anywhere
Now you can use the form anywhere in your app. You are all set so go ahead any try it out.
import SendEmailForm from "./components/SendEmailForm";
export default function Home() {
return (
<div className="flex items-center justify-center w-full h-[60vh]">
<SendEmailForm />
</div>
);
}