
How to Send Emails in Next.js Using Resend, Prisma, and MongoDB (Production-Ready Guide)
Suman Kumar Keshari
Founder of Skilldham and Software Engineer
If you’ve ever tried sending emails from a Next.js app, you’ve probably run into one of these problems:
SMTP configuration nightmares
Emails landing in spam
Serverless environments breaking traditional email libraries
No easy way to store subscribers
This guide shows a clean production approach using:
Next.js (App Router)
Prisma ORM
MongoDB
Resend (modern email API)
By the end, you’ll have a working email subscription system that:
Stores users in MongoDB
Sends verification emails
Uses Resend’s API
Works perfectly on Vercel
No hacks. No fragile SMTP configs.
Project Architecture
We’ll build a simple but real production flow:
User enters email
↓
Next.js API route
↓
Prisma stores email in MongoDB
↓
Resend sends verification email
↓
User confirms subscriptionStep 1 — Create a Next.js Project
If you don’t already have one:
npx create-next-app@latest newsletter-appChoose:
✔ TypeScript
✔ App Router
✔ ESLint
✔ Tailwind (optional)Go inside the project:
cd newsletter-appStep 2 — Install Required Packages
Install dependencies:
npm install prisma @prisma/client resendThen initialize Prisma:
npx prisma initThis creates:
/prisma/schema.prisma
.envStep 3 — Configure MongoDB
Update .env:
DATABASE_URL="mongodb+srv://username:password@cluster.mongodb.net/newsletter"
RESEND_API_KEY="your_resend_api_key"You can get MongoDB connection string from MongoDB Atlas.
Step 4 — Configure Prisma Schema
Open:
prisma/schema.prismaUse this schema:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model Subscriber {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
verified Boolean @default(false)
createdAt DateTime @default(now())
}Generate Prisma client:
npx prisma generateStep 5 — Create Prisma Client
Create a helper file:
/lib/prisma.tsimport { PrismaClient } from "@prisma/client";
const globalForPrisma = global as unknown as {
prisma: PrismaClient;
};
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: ["query"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;This prevents multiple Prisma instances during development.
Step 6 — Setup Resend
Create:
/lib/resend.tsimport { Resend } from "resend";
export const resend = new Resend(process.env.RESEND_API_KEY);Step 7 — Create Subscribe API
Create:
/app/api/subscribe/route.tsimport { prisma } from "@/lib/prisma";
import { resend } from "@/lib/resend";
import { NextResponse } from "next/server";
export async function POST(req: Request) {
try {
const { email } = await req.json();
if (!email) {
return NextResponse.json(
{ error: "Email required" },
{ status: 400 }
);
}
const subscriber = await prisma.subscriber.create({
data: { email },
});
await resend.emails.send({
from: "Newsletter <onboarding@resend.dev>",
to: email,
subject: "Welcome to our newsletter",
html: `
<h2>Welcome!</h2>
<p>You have successfully subscribed.</p>
`,
});
return NextResponse.json({
success: true,
subscriber,
});
} catch (error) {
return NextResponse.json(
{ error: "Something went wrong" },
{ status: 500 }
);
}
}Step 8 — Create Frontend Form
Create a simple form:
/app/page.tsx"use client";
import { useState } from "react";
export default function Home() {
const [email, setEmail] = useState("");
const [status, setStatus] = useState("");
const handleSubmit = async (e: any) => {
e.preventDefault();
const res = await fetch("/api/subscribe", {
method: "POST",
body: JSON.stringify({ email }),
});
const data = await res.json();
if (data.success) {
setStatus("Subscribed successfully!");
setEmail("");
} else {
setStatus("Something went wrong");
}
};
return (
<main style={{ padding: "40px" }}>
<h1>Newsletter</h1>
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="Enter email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<button type="submit">
Subscribe
</button>
</form>
<p>{status}</p>
</main>
);
}Step 9 — Run the App
Start development server:
npm run devVisit:
http://localhost:3000Enter an email → subscriber stored in MongoDB → email sent via Resend.
Step 10 — Deploy on Vercel
Push project to GitHub.
Then deploy:
vercelAdd environment variables:
DATABASE_URL
RESEND_API_KEYDone.
Why This Stack Works Well
Next.js Handles API + frontend together.
Prisma Clean database layer.
MongoDB Perfect for subscriber data.
Resend Reliable transactional email without SMTP headaches.
Final Thoughts
Email infrastructure used to be painful.
Modern tools like Resend + Next.js + Prisma make it much simpler to build reliable email systems.
The key is keeping the architecture simple:
Store subscribers
Send email
Handle responses gracefully
Everything else can evolve later.
Frequently Asked Questions
1. Why use Resend for sending emails in a Next.js app? Resend provides a simple API that works well with serverless platforms like Vercel. It avoids traditional SMTP issues and improves reliability and deliverability.
2. Can I send transactional emails like verification or password reset with Resend? Yes. Resend is built for transactional emails such as account verification, password resets, notifications, and newsletters, and integrates easily with Next.js API routes.
3. Why combine Prisma with MongoDB in a Next.js project? Prisma provides a clean and type-safe way to interact with the database. Using it with MongoDB simplifies data modeling, queries, and overall database management.
4. Is this setup production-ready for real applications? Yes. The combination of Next.js API routes, Prisma for database management, MongoDB for storage, and Resend for email delivery is reliable for production use.