In this article, you’ll learn how to add a contact form to your Next.js SSR application (with TypeScript). We will use the Brevo API to send emails in JavaScript. Brevo is one of the most popular Email Service Providers (ESPs), to send emails.
Why Should You Use Brevo for sending emails in JavaScript?
Using Brevo's API, you can send transactional emails or you can automate your email marketing directly within your app, bypassing the need for a separate interface. Email marketing remains one of the most cost-effective channels for customer engagement.
If you haven’t signed up for Brevo yet, you can create an account here and start exploring its API.
Brevo (formerly known as Sendinblue) is a robust, all-in-one marketing platform, offering tools for email and SMS campaigns, marketing automation, and much more. Its API is well-documented and flexible, allowing you to fully control email sending from your application while integrating seamlessly with your tech stack.
To follow along, make sure you have:
-
A Brevo account Create free account Compare plans
-
A Next.js application set up and running
-
Access to your Brevo API key, which you can find in your Brevo account under SMTP & API
Step-by-Step Guide to Sending an Email with Brevo API in Next.js
Step 1: Install Axios
We’ll use Axios to send HTTP requests to Brevo’s API from Next.js. If you haven’t installed Axios yet, you can do so by running:
npm install axios
Configure Brevo API Key
Create your API key in Brevo's Dashboard Settings > Keys > API
In the root of your Next.js project, create a .env
file (or addd to existing one) to securely store your Brevo API key:
BREVO_API_KEY=your_brevo_api_key_here
Set Up the Email-Sending Function
Create a function to handle sending emails using Brevo's API. Inside your Next.js project, create a new file in the /pages/api directory
, such as /pages/api/contact.js
import axios from "axios";
export default async function handler(req, res) {
if (req.method === "POST") {
const { name, email, message } = req.body;
try {
const response = await axios.post(
"https://api.brevo.com/v3/smtp/email",
{
sender: {
name: process.env.TARGET_NAME,
email: process.env.TARGET_EMAIL,
},
to: [{ email: process.env.TARGET_EMAIL }],
subject: "Contact Form Submission",
htmlContent: `<html>
<body>
<h1>Contact Form Submission</h1>
<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Message:</strong></p>
<p>${message}</p>
</body>
</html>
`,
},
{
headers: {
"api-key": process.env.BREVO_API_KEY,
"Content-Type": "application/json",
accept: "application/json",
},
}
);
return res
.status(200)
.json({ message: "Email sent successfully!", data: response.data });
} catch (error) {
console.error("Error sending email:", error);
return res
.status(500)
.json({ error: "Failed to send email", details: error.message });
}
} else {
res.setHeader("Allow", ["POST"]);
return res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
import axios from "axios";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === "POST") {
const { name, email, message } = req.body;
try {
const response = await axios.post(
"https://api.brevo.com/v3/smtp/email",
{
sender: {
name: process.env.TARGET_NAME as string,
email: process.env.TARGET_EMAIL as string,
},
to: [{ email: process.env.TARGET_EMAIL as string }],
subject: "Contact Form Submission",
htmlContent: `<html>
<body>
<h1>Contact Form Submission</h1>
<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Message:</strong></p>DDD
<p>${message}</p>
</body>
</html>
`,
},
{
headers: {
"api-key": process.env.BREVO_API_KEY as string,
"Content-Type": "application/json",
accept: "application/json",
},
}
);
return res
.status(200)
.json({ message: "Email sent successfully!", data: response.data });
} catch (error: any) {
console.error("Error sending email:", error);
return res
.status(500)
.json({ error: "Failed to send email", details: error.message });
}
} else {
res.setHeader("Allow", ["POST"]);
return res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
Create a Form
You can create a form in a component (e.g., components/EmailForm.js
) that will submit data to this API endpoint.
"use client";
import { useState } from "react";
export default function EmailForm() {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [message, setMessage] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, name, message }),
});
if (response.ok) {
alert("Email sent successfully!");
} else {
alert("Failed to send email.");
}
} catch (error) {
console.error("Error:", error);
alert("An error occurred.");
}
};
return (
<form
onSubmit={handleSubmit}
className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md"
>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e)=> setName(e.target.value)}
required
className="w-full px-4 py-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e)=> setEmail(e.target.value)}
required
className="w-full px-4 py-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<textarea
placeholder="Your Message"
value={message}
onChange={(e)=> setMessage(e.target.value)}
required
className="w-full px-4 py-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
rows={4}
></textarea>
<button
type="submit"
className="w-full px-4 py-2 text-white bg-blue-600 rounded-md hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
>
Send
</button>
</form>
);
}
"use client";
import { useState } from "react";
export default function EmailForm() {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [message, setMessage] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, name, message }),
});
if (response.ok) {
alert("Email sent successfully!");
} else {
alert("Failed to send email.");
}
} catch (error) {
console.error("Error:", error);
alert("An error occurred.");
}
};
return (
<form
onSubmit={handleSubmit}
className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md"
>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e)=> setName(e.target.value)}
required
className="w-full px-4 py-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e)=> setEmail(e.target.value)}
required
className="w-full px-4 py-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<textarea
placeholder="Your Message"
value={message}
onChange={(e)=> setMessage(e.target.value)}
required
className="w-full px-4 py-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
rows={4}
></textarea>
<button
type="submit"
className="w-full px-4 py-2 text-white bg-blue-600 rounded-md hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
>
Send
</button>
</form>
);
}
Troubleshooting Email Sending Issues
If you're getting 403 error, it might be this one:
{"message":"Unable to send email. Your SMTP account is not yet activated. Please contact us at contact@sendinblue.com to request activation","code":"permission_denied"}
Your account hasn't been activated yet. You may encounter similar issue when trying to send from the user interface:
You need to complete your account creation and receive a confirmation email stating, 'Your account has been validated.'
Sign Up to get a ZIP with complete working demo app
Tech stack: Next.js 15 (SSR mode), TypeScript, Axios, Tailwind.
Sending emails in JavaScript often takes place on public websites, which makes such contact forms vulnerable to spam bots. If they find a form on your site, they will attempt to fill it out and submit it.
Next, we will implement additional security checks. Currently, there is only one check in place: the form will not work without JavaScript enabled. However, this is definitely not enough to prevent spam. Subscribe to stay informed about updates.