How to Send Email in Next.js with AWS SES and Netlify Functions

Web Development11 min read

Learn how to send transactional email from Next.js with AWS SES and Netlify Functions, including domain verification, SMTP setup, contact forms, security, and deployment.


A practical way to send transactional email from a Next.js application hosted on Netlify is to use Amazon SES for email delivery and a Netlify Function as the server-side sending layer. This keeps your email credentials away from the browser and gives you a simple path for contact-form notifications, transactional messages, and other application emails.

This guide walks through the setup from domain verification to deployment, explains the important security decisions, and shows a working implementation using Nodemailer and AWS SES SMTP.

How the Setup Works

The basic architecture is:

Next.js form
      ↓
Netlify Function
      ↓
AWS SES
      ↓
Recipient's inbox

The browser collects the form data, but the actual email request is handled server-side.

That separation matters because SMTP usernames, passwords and other email credentials should never be exposed in client-side JavaScript.

You can also use the same pattern for:

  • Contact-form notifications
  • Customer confirmations
  • Transactional messages
  • Download confirmations
  • Application alerts
  • PDF or resource delivery

The exact architecture can change depending on your hosting provider and application requirements, but the principle is the same: keep email credentials and sending logic on the server side.

Why Use AWS SES with Netlify Functions?

SES can be a good fit when you want a transactional email service with usage-based pricing and direct control over your sending setup.

Netlify Functions provide the server-side execution layer without requiring you to run a separate application server just to process a contact form.

This combination can work well when:

  • Your Next.js application is already deployed on Netlify.
  • You need transactional email rather than a full marketing platform.
  • You want to manage the sending identity yourself.
  • You are comfortable working with AWS and DNS configuration.
  • You want the email logic to remain close to your application code.

It is not the only valid choice. Services such as Postmark, Resend, SendGrid and other providers may be more appropriate depending on your team's priorities, existing infrastructure and email requirements.

The goal is to choose the simplest reliable setup for the application rather than assuming one provider is best for every project.

Part 1: Set Up AWS SES

1. Choose an SES Region

SES is region-specific, so choose a region that fits your deployment, operational and compliance requirements.

Once you choose the region, keep your verified identities and sending configuration consistent with that region.

2. Verify Your Domain

In the AWS console, open Amazon SES and create a verified identity for your sending domain.

AWS will provide DNS records for domain verification and DKIM.

Add the required records to the DNS provider that manages your domain.

For example:

yourdomain.com
hello@yourdomain.com

A verified domain is preferable to relying on a single verified address because it gives you a consistent sending identity for the application.

3. Configure Email Authentication

Follow the DNS instructions provided by SES for your identity.

In addition to DKIM, make sure your overall domain authentication strategy is configured correctly for your email setup. SPF and, where appropriate, DMARC are important parts of a healthy sending configuration.

Do not assume that verifying an SES identity alone guarantees inbox placement. Authentication is one part of sender reputation and deliverability.

4. Request Production Access

New SES accounts begin in sandbox mode.

While in sandbox, sending is restricted. To use SES for real production recipients, request production access and describe your actual use case honestly.

For a contact form, your explanation might mention:

  • Contact-form notifications
  • Transactional confirmations
  • User-initiated requests
  • Expected sending volume
  • How you handle bounces and complaints

AWS assigns sending limits based on your account and use case, so do not hard-code an assumed production quota into your application design.

For current limits and pricing, check the official AWS documentation before estimating capacity or cost:

Amazon SES pricing

Amazon SES quotas

Part 2: Configure Netlify

Project Structure

A simple project can use a Netlify Functions directory such as:

your-nextjs-app/
├── app/
├── public/
├── netlify/
│   └── functions/
│       └── contactMail.js
├── netlify.toml
└── package.json

The exact structure can differ depending on your Next.js and Netlify setup. Follow the current Netlify Functions conventions for your project.

Environment Variables

Store credentials in your Netlify environment configuration, not in source control.

For example:

SES_SMTP_USER=your_smtp_username
SES_SMTP_PASS=your_smtp_password
INTERNAL_RECEIVER_EMAIL=hello@yourdomain.com

For local development, use a local environment file that is excluded from Git.

Never commit SMTP credentials to your repository.

Install Nodemailer

If you are using SES SMTP with Nodemailer:

npm install nodemailer

Nodemailer handles the SMTP connection and message construction while SES handles the delivery infrastructure.

Part 3: Build the Server-Side Function

A simplified contact function can look like this:

const nodemailer = require("nodemailer");

exports.handler = async function (event) {
  if (event.httpMethod !== "POST") {
    return {
      statusCode: 405,
      body: JSON.stringify({ error: "Method not allowed" }),
    };
  }

  try {
    const { name, email, phone, message } = JSON.parse(event.body || "{}");

    if (!name || !email || !message) {
      return {
        statusCode: 400,
        body: JSON.stringify({ error: "Missing required fields" }),
      };
    }

    const transporter = nodemailer.createTransport({
      host: process.env.SES_SMTP_HOST,
      port: Number(process.env.SES_SMTP_PORT || 465),
      secure: true,
      auth: {
        user: process.env.SES_SMTP_USER,
        pass: process.env.SES_SMTP_PASS,
      },
    });

    await transporter.sendMail({
      from: '"Your Brand" <hello@yourdomain.com>',
      to: process.env.INTERNAL_RECEIVER_EMAIL,
      replyTo: email,
      subject: `New website enquiry from ${name}`,
      text: `${message}\n\nPhone: ${phone || "Not provided"}`,
    });

    return {
      statusCode: 200,
      body: JSON.stringify({ success: true }),
    };
  } catch (error) {
    console.error("Contact mail error:", error);

    return {
      statusCode: 500,
      body: JSON.stringify({ error: "Unable to send message" }),
    };
  }
};

This example is intentionally simple.

For production, you should also consider:

  • Stronger server-side validation
  • Request size limits
  • Rate limiting
  • Bot protection
  • Input sanitization
  • Structured error logging
  • Retries or queueing for important workloads
  • Monitoring and alerting

Why replyTo matters

Keep the from address on your verified domain and use the visitor's address as replyTo.

That way, the message is sent from an identity your mail system controls while your team can still reply directly to the person who submitted the form.

Part 4: Connect the Next.js Form

Your client component can call the server-side function:

"use client";

const handleSubmit = async (event) => {
  event.preventDefault();

  const response = await fetch("/.netlify/functions/contactMail", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name,
      email,
      phone,
      message,
    }),
  });

  const data = await response.json();

  if (data.success) {
    // Show success state
  } else {
    // Show error state
  }
};

The important point is that the browser does not receive the SES credentials. It only calls your server-side function.

Part 5: Protect the Contact Endpoint

A public contact form is an easy target for automated spam.

Email delivery should therefore not be the only consideration.

At minimum:

Validate on the server

Never rely only on browser validation.

Check:

  • Required fields
  • Email format
  • Message length
  • Request size
  • Unexpected input

Add rate limiting

A single IP or client should not be able to submit hundreds of messages in a short period.

Add bot protection where necessary

Depending on the volume of spam, you may use CAPTCHA, a managed challenge, honeypot fields, behavioral checks, or another appropriate control.

Avoid reflecting unsanitized input

Do not place submitted names or message content directly into HTML email without escaping or sanitizing it correctly.

This matters both for security and for reliable email rendering.

Part 6: Email Templates

Email HTML is different from normal website HTML.

Email clients have varying levels of support for modern CSS, so conservative layouts and inline styles are still common when consistent rendering matters.

For important transactional emails:

  • Keep the layout simple.
  • Use clear typography.
  • Use responsive widths.
  • Include a useful preheader.
  • Test links and images.
  • Avoid depending on advanced CSS features.
  • Test across the clients your customers actually use.

For example, a simple branded email can use a centered table layout with inline styles:

<table
  role="presentation"
  width="100%"
  cellpadding="0"
  cellspacing="0"
  border="0"
>
  <tr>
    <td align="center" style="padding:24px;background:#f3f4f6;">
      <table
        role="presentation"
        width="100%"
        cellpadding="0"
        cellspacing="0"
        border="0"
        style="max-width:520px;background:#ffffff;"
      >
        <tr>
          <td style="padding:32px;font-family:Arial,sans-serif;color:#222;">
            <h1 style="margin:0 0 16px;font-size:24px;">
              Thanks for getting in touch
            </h1>

            <p style="margin:0;line-height:1.6;">
              We received your message and will get back to you soon.
            </p>
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>

What about dark mode?

Email clients can apply their own dark-mode transformations.

There is no single CSS trick that guarantees identical rendering everywhere, so test the actual emails in the clients that matter to your audience.

That is more reliable than assuming a specific dark-mode override will behave identically across every client.

Part 7: Attachments and PDFs

SES can send messages with attachments through the SMTP/Node.js layer.

For example:

attachments: [
  {
    filename: "guide.pdf",
    path: "/tmp/guide.pdf",
  },
]

But large attachments can increase message size and may be restricted by some mail systems.

For larger resources, a secure download link is often a better experience than attaching the entire file.

Part 8: Test Before Production

Before launching, verify:

  • SES domain identity is verified
  • DKIM is configured
  • Production access is approved if required
  • SMTP credentials are stored securely
  • Environment variables exist in the production environment
  • The form works from the deployed website
  • Internal notification emails arrive
  • Reply-to behavior works
  • Error states are handled
  • Spam protection is enabled
  • Emails render correctly on the clients you care about

Also test failure cases.

For example:

What happens if SES is unavailable?

What happens if the visitor submits a 5 MB message?

What happens if the same IP submits 100 requests?

A production email flow needs to handle failure gracefully rather than exposing implementation errors to the user.

Common Problems

ProblemLikely causeWhat to check
535 authentication errorIncorrect SMTP credentialsRecreate or recheck the SES SMTP credentials
Recipient cannot receive emailSES sandbox or account restrictionCheck SES account status and sending permissions
Connection timeoutWrong SMTP host/port or network issueVerify the SES endpoint for your region
429 or throttlingSending rate exceededCheck your SES quota and add queueing/backoff if needed
Missing form fieldsWeak request validationValidate the payload server-side
Emails look brokenClient-specific HTML/CSS behaviorSimplify the template and test actual clients
Contact form is full of spamNo abuse protectionAdd rate limiting and bot controls

What Does This Stack Cost?

Do not rely on a fixed monthly number for an AWS/Netlify email stack.

AWS SES pricing depends on your current SES pricing plan and usage, while Netlify costs depend on the site plan and function usage.

For current pricing, check:

Amazon SES pricing

Netlify pricing

The important question is not simply “Which email provider is cheapest?”

Look at:

  • Sending volume
  • Delivery requirements
  • Provider features
  • Operational overhead
  • Existing infrastructure
  • Monitoring and support
  • Compliance requirements

How CorgenX Uses This Kind of Architecture

We have used Next.js, server-side email handling and transactional messaging patterns in production projects for contact forms, notifications and resource delivery.

The exact implementation varies by project. We choose the email provider and application architecture based on the project's volume, hosting environment, security requirements, and operational needs rather than forcing every project onto the same stack.

That distinction matters because AWS SES + Netlify is one workable option, not a universal rule for every Next.js application.

Explore Web Development Services

Explore Custom Web Applications

FAQs

Can I send email from Next.js without exposing SMTP credentials?

Yes. The email-sending logic should run on the server side, such as a Netlify Function or another backend service. The browser should never receive your SMTP credentials.

Is AWS SES a good choice for a Next.js application?

It can be a good choice when you want a transactional email service with direct control over the sending setup and are comfortable managing AWS configuration. Other providers may be simpler depending on the project.

Can I use AWS SES on Netlify?

Yes. A Netlify Function can handle the server-side request and send email through SES, provided the SES account and credentials are configured correctly.

Do I need Nodemailer?

No. Nodemailer is one way to send email through SMTP from Node.js. You can also use the AWS SDK or another supported approach depending on your architecture.

Why are my emails going to spam?

Authentication is only one part of deliverability. Sender reputation, message content, recipient engagement, bounce and complaint rates, domain configuration, and mailbox-provider policies can all matter.

Should I use SMTP or the AWS SDK?

Both can work. SMTP through Nodemailer can be convenient when your application already uses Nodemailer-style message construction. The AWS SDK can be preferable when you want to interact with SES directly through AWS APIs.

Can I send PDFs or other attachments?

Yes, but keep attachment size and recipient-mailbox restrictions in mind. For larger resources, a secure download link is often better.

Is this setup suitable for every Next.js project?

No. The right approach depends on the application's hosting, email volume, security needs, operational requirements, and existing infrastructure.

Back to all articles

Related posts

Contact Us

Have a Website or Digital Project in Mind?

Tell us what you're trying to build, improve, migrate or scale. We'll understand the requirement and recommend the right approach.

Get a Free Website Audithello@corgenx.com

Fill in your details and we’ll reach out to you within 24h