Site icon Mailtrap

How to Send Emails in Next.js using Client & Server-Side Methods: A Complete Guide

In this tutorial, I’ll show you how to add an email-sending functionality for your Next.js web application and make it send messages from contact forms, user notifications, password resets, and more. 

If you feel like jumping ahead, here are the chapters:

And before setting up a Next.js, make sure you have the necessary prerequisites:

Disclaimer: Every bit of code in this article has been prepared and tested by a developer before publication.

Ready to deliver your emails?
Try Mailtrap for Free

Set up a Next.js project

Before diving into email functionality, let’s set up a fresh Next.js project using the latest stable version (as of May 2025) with the App Router structure. 

1. Create a new Next.js app

For this step, you can use the Next.js official CLI tool create-next-app. Simply run one of the commands below and replace “my-next-app” with your desired project name.

npx:

npx create-next-app@latest my-next-app

yarn:

yarn create next-app my-next-app

Both of the commands above will launch an interactive prompt in your terminal, which will ask you a series of questions. For this demo, use the following choices, but also feel free to use the options that work for you best:

✔ Would you like to use TypeScript? … No  
✔ Would you like to use ESLint? … Yes   
✔ Would you like to use Tailwind CSS? … No   
✔ Would you like your code inside a `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to use Turbopack for `next dev`? … Yes   
✔ Would you like to customize the import alias (`@/*` by default)? … No

2. Navigate to the project and review the structure

Once the setup is complete, navigate into your project directory:

cd my-next-app

Your Next.js app’s folder structure should look something like this:

my-next-app/
|── app/
│   |── favicon.ico
│   |── globals.css
│   |── layout.js
│   |── page.js
│  └── page.module.css
|── public/
│   |── file.svg
│  └── …other svg files
|── .gitignore
|── eslint.config.mjs
|── jsconfig.json
|── package.json
|── README.md
└── next.config.js

Now, let me explain some directories and files real quick so you get a better picture:

For our purposes, we will organize email-related code as follows:

3. Run the development server

Lastly, verify everything is set up correctly by running the dev server:

npm:

npm run dev

yarn:

yarn dev

This starts the development server. Open your browser to http://localhost:3000. You should see the default Next.js welcome page (with the heading “Welcome to Next.js” or some starter content). This confirms your project is up and running. 

For now, you can stop the server with Ctrl+C and proceed with implementing the email functionality. 

Send email without a backend using EmailJS

EmailJS is a service that allows you to send emails directly from JavaScript by connecting your client-side app to providers, such as Gmail, Outlook, Mailtrap — all without setting up your own server or SMTP. This makes it a perfect choice for static sites or front-end-only applications where you want a quick way to send form submissions via email.

Now, let’s set it up and send some emails!

1. Setting up EmailJS

Before writing any code, follow these steps to configure EmailJS:

Go to the EmailJS website and create a free account. You can use the free tier for basic purposes.

Navigate to the ‘Account’ page on the sidebar menu on your left. Then, in the Notifications section, insert your Mailtrap sending domain (e.g., no-reply@my.own.domain). 

In the EmailJS dashboard, navigate to Email Services and select Mailtrap.

Next, configure the service by providing the following details:

Username and Password: Find your credentials by going to your Mailtrap dashboard and then navigating to Sending Domains > Integration > SMTP.

Service ID: A unique identifier for this service. You can name it something like “mailtrap_service” or any name you like.

Email Feature – Select “Sending” from the dropdown menu.

Important: Don’t mark the ‘Send test email to verify configuration’ checkbox since then the whole setup and configuration won’t work.

With all the details provided, click ‘Create Service’ to finalize this step and have the service listed in your EmailJS dashboard.

In the EmailJS dashboard, go to Email Templates and click “Create New Template”. In the template editor, you’ll set up the email that will be sent out. This includes:

Subject of the email (for example, “New Contact Form Submission”).

Body content of the email. You can write a message and use placeholders for form fields (e.g., {{name}}, {{message}} where you want to insert the sender’s name or message).

→ Dynamic data such as {{name}}, {{email}}, and {{message}} where the user’s input will go. 

Pro tip: If you include HTML in your template content, wrap placeholders with triple braces like {{{message}}} to properly render HTML content.

Note: You can also configure advanced options like auto-reply, add attachments, etc., but for a simple use case, keep it basic.

Important:

You can also check out how your template looks by clicking on the ‘Test it’ button. 

This will open a new popup window where you need to provide some details, such as the service used and the values for template parameters. ⬇️

If everything goes as expected, you will get “200 OK” as a result, meaning the email was sent successfully. 

Once you’ve set up the service and template, make sure to save your Service ID, Template ID, and EmailJS Public Key. You can find these in your EmailJS dashboard. This step is important since we’ll use these in the Next.js code to initialize EmailJS and send emails. 

2. Installing the EmailJS Client SDK

Now, let’s install a JavaScript SDK (provided by EmailJS) by running one of the following commands:

npm:

npm install @emailjs/browser

yarn:

yarn add @emailjs/browser

(The @emailjs/browser package is the official EmailJS SDK for browser environments.)

3. Configuring environment variables

Since we’ll be using some API keys and credentials for EmailJS and Mailtrap, I advise against hardcoding sensitive information. 

Instead, I recommend using an .env.local file in the project root to store environment variables. This way, Next.js will automatically load variables from this file into process.env and it will be git-ignored by default to keep secrets safe. 

To do this, simply create a file named .env.local in the root of my-next-app/ and add the following:

# EmailJS credentials
NEXT_PUBLIC_EMAILJS_PUBLIC_KEY=your_emailjs_public_key
NEXT_PUBLIC_EMAILJS_SERVICE_ID=your_service_id
NEXT_PUBLIC_EMAILJS_TEMPLATE_ID=your_template_id

Note: The NEXT_PUBLIC_ prefix ensures these will be available in the browser environment.

4. Initializing EmailJS in a React component 

Before we write the code to send emails, we need to initialize EmailJS with our public key, which lets EmailJS verify your client requests. For example, in a plain HTML/JS environment, you might include a script and call emailjs.init(‘YOUR_PUBLIC_KEY’) as shown in their official documentation

In our Next.js app, we can do this in our React component code. To keep things organized, create a new file: app/components/ContactForm.js (you may need to create the components folder inside app/). This will be a React component that includes a form and uses EmailJS to send the form data.

Then, open app/components/ContactForm.js and add the following code:

"use client";  // This directive is important to make this a Client Component (can use browser APIs)

import { useRef } from 'react';
import emailjs from '@emailjs/browser';

// Initialize EmailJS with your Public Key
emailjs.init(process.env.NEXT_PUBLIC_EMAILJS_PUBLIC_KEY);

export default function ContactForm() {
  // useRef to access the form DOM node
  const formRef = useRef();

  const sendEmail = (e) => {
    e.preventDefault(); // prevent default form submission behavior
    if (!formRef.current) return;
    const form = formRef.current;

    // Use EmailJS to send form data
    emailjs.sendForm(
      process.env.NEXT_PUBLIC_EMAILJS_SERVICE_ID,
      process.env.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID,
      form
    )
    .then((response) => {
      console.log('SUCCESS!', response.status, response.text);
      // You could show a success message to the user here
    })
    .catch((error) => {
      console.error('FAILED...', error);
      // Show an error message to the user if needed
    });
    
    // Optionally, reset the form or handle UI state after sending
    form.reset();
  };

  return (
    <form ref={formRef} onSubmit={sendEmail}>
      <label>
        Name:
        <input type="text" name="name" required />
      </label>
      <br/>
      <label>
        Your Email:
        <input type="email" name="email" required />
      </label>
      <br/>
      <label>
        Message:
        <textarea name="message" rows="4" required></textarea>
      </label>
      <br/>
      <button type="submit">Send Message</button>
    </form>
  );
}

Some notes:

5. Using the form in a Next.js page

Now, we need to use this ContactForm component in a page so that it’s rendered. 

For example, let’s place it on app/page.js, which is the index page:

import ContactForm from "./components/ContactForm";

export default function Page() {
  return (
    <main>
      <h1>Contact Us</h1>
      <p>Fill out the form below to send us a message:</p>
      <ContactForm />
    </main>
  );
}

Save the files and restart the dev server with npm run dev or yarn dev if not already running. 

Then, open http://localhost:3000, and you should see your contact form. Try filling it out and submitting it. 

On submission, if everything is configured correctly, the form should trigger the EmailJS send. In your terminal/console you should see a log “SUCCESS! 200 …” from the console.log in our code, indicating EmailJS accepted the request. 

Send email with a backend using SMTP

For a server-side approach, we’ll use Next.js API routes (backend endpoints) to send emails using Node.js. The idea is to have the client (browser) make a request to a Next.js API route (for example, when a form is submitted), and that route will handle sending the email. This allows more complex email logic (like using libraries or interacting with databases) securely.

For this, we’ll use Nodemailer, a Node.js library that makes it easy to connect to an SMTP server via credentials and start sending emails right away. 

1. Install Nodemailer

To install Nodemailer, you can either use npm:

npm install nodemailer

Or yarn:

yarn add nodemailer

2. Update SMTP credentials

Before we write any code, let’s update the .env.local file to include our Mailtrap SMTP credentials:

# EmailJS credentials
NEXT_PUBLIC_EMAILJS_PUBLIC_KEY=your_emailjs_public_key
NEXT_PUBLIC_EMAILJS_SERVICE_ID=your_service_id
NEXT_PUBLIC_EMAILJS_TEMPLATE_ID=your_template_id

# Mailtrap SMTP credentials
MAILTRAP_USER=api
MAILTRAP_PASS=your_mailtrap_smtp_password

Note: Did you notice how we don’t use the NEXT_PUBLIC_ prefix for these? Our MAILTRAP_USER and MAILTRAP_PASS environment variables will be used on the server side, so we don’t actually need to use the public prefix to expose them to the browser environment. This guarantees that the environment variables won’t be leaked to the client side.

3. Add email-sending logic

Now, let’s create a Next.js API route for sending email with Nodemailer. Create a file app/api/contact/route.js in your project:

// app/api/contact/route.js
import { NextResponse } from 'next/server';
import nodemailer from 'nodemailer';

export async function POST(request) {
  try {
    const data = await request.json();  // parse JSON body from the request
    const { name, email, message } = data;

    // Set up Nodemailer transporter with Mailtrap SMTP credentials
    const transporter = nodemailer.createTransport({
      host: "live.smtp.mailtrap.io",  // Mailtrap SMTP host
      port: 587,                        // Mailtrap SMTP port
      auth: {
        user: process.env.MAILTRAP_USER, // SMTP username from .env.local
        pass: process.env.MAILTRAP_PASS  // SMTP password from .env.local
      }
    });

    // Email message options
    const mailOptions = {
      from: `"Contact Form" <noreply@yourdomain.com>`, // sender address
      to: "your-email@example.com",    // your email or whoever should get the contact message
      subject: "New Contact Form Submission",
      text: `You have a new message from ${name} (${email}):\n\n${message}`
      // You can add html: "<p>...</p>" to send HTML emails as well
    };

    // Send the email
    await transporter.sendMail(mailOptions);

    return NextResponse.json({ ok: true, status: "Email sent successfully" });
  } catch (error) {
    console.error("Error sending email:", error);
    return NextResponse.json({ ok: false, error: error.message }, { status: 500 });
  }
}

Let’s explain this route handler:

Now, your Next.js backend can send an email via SMTP whenever this /api/contact endpoint is hit with a POST request. 

4. Use the API route

To trigger the sending logic from the client side, you need to make a POST request to /api/contact with the appropriate data. 
For example, in your React component (perhaps the same contact form, if you want to now use the server approach instead of EmailJS), you could change the app/components/ContactForm.js file accordingly:

// Example: ContactForm using server API instead of EmailJS
"use client";
import { useState } from 'react';

export default function ContactFormServer() {
  const [status, setStatus] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = {
      name: e.target.name.value,
      email: e.target.email.value,
      message: e.target.message.value
    };
    try {
      const res = await fetch('/api/contact', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(formData)
      });
      const result = await res.json();
      if (res.ok && result.ok) {
        setStatus('Your message has been sent!');
        e.target.reset();
      } else {
        throw new Error(result.error || 'Failed to send message');
      }
    } catch (err) {
      console.error(err);
      setStatus('Sorry, there was an error sending your message.');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name: <input type="text" name="name" required/>
      </label><br/>
      <label>
        Your Email: <input type="email" name="email" required/>
      </label><br/>
      <label>
        Message: <textarea name="message" required></textarea>
      </label><br/>
      <button type="submit">Send Message</button>
      {status && <p>{status}</p>}
    </form>
  );
}

This is a basic example: on submit, it posts to our /api/contact route, then handles the JSON response. If successful, it displays a success message and resets the form; if there’s an error, it shows an error message. 

Send HTML email 

With Nodemailer, sending an HTML email is as easy as adding an html field in the mailOptions in your app/api/contact/route.js file. Check it out:

// ... (transporter setup as above)

const mailOptions = {
    from: `"Contact Form" <noreply@yourdomain.com>`, // sender address
    to: "your-email@example.com", // your email or whoever should get the contact message
    subject: "New Contact Form Submission",
    html: `
        <h1 style="color: teal;">Welcome to MyApp,</h1>
        <p>Hi there, thanks for <b>joining MyApp</b>! We're excited to have you on board.</p>
        <p>Feel free to explore our features and let us know if you have any questions.</p>
    `,
    // Note: We can still include a "text" field as a fallback for email clients that prefer plain text.
};

await transporter.sendMail(mailOptions);

Pro tip: It’s a good practice to also include a plain text fallback mailOptions.text for email clients that cannot render HTML.

Send email with attachment

If you want to send files via email (e.g., monthly reports, invoices, etc.), Nodemailer supports attachments via the attachments option in the mailOptions object, which you can update in the app/components/ContactForm.js file:

// Example: ContactForm using server API
"use client";
import { useState } from "react";

export default function ContactFormServer() {
  const [status, setStatus] = useState(null);

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

    const formData = new FormData();
    formData.append("name", e.target.name.value);
    formData.append("email", e.target.email.value);
    formData.append("message", e.target.message.value);

    // Handle multiple file attachments properly
    [...e.target.attachments.files].forEach((file) =>
      formData.append("attachments", file)
    );

    try {
      const res = await fetch("/api/contact", {
        method: "POST",
        // Don't set Content-Type when using FormData - browser will set it automatically
        // with the correct boundary parameter
        body: formData,
      });
      const result = await res.json();
      if (res.ok && result.ok) {
        setStatus("Your message has been sent!");
        e.target.reset();
      } else {
        throw new Error(result.error || "Failed to send message");
      }
    } catch (err) {
      console.error(err);
      setStatus("Sorry, there was an error sending your message.");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name: <input type="text" name="name" required />
      </label>
      <br />
      <label>
        Your Email: <input type="email" name="email" required />
      </label>
      <br />
      <label>
        Message: <textarea name="message" required></textarea>
      </label>
      <br />
      <label>
        Attachments: <input type="file" name="attachments" multiple />
      </label>
      <br />
      <button type="submit">Send Message</button>
      {status && <p>{status}</p>}
    </form>
  );
}

Quick breakdown:

Then, in the app/api/contact/route.js, modify the mailOptions object to include the new attachments property with the attachment data details in it as well as add the new logic to parse out the file attachments from the multipart/form-data object. Just like so:

// app/api/contact/route.js
import { NextResponse } from "next/server";
import nodemailer from "nodemailer";

export async function POST(request) {
  try {
    const formData = await request.formData();

    const name = formData.get("name");
    const email = formData.get("email");
    const message = formData.get("message");

    // Process attachments - convert File objects to buffers that Nodemailer can handle
    const fileAttachments = formData.getAll("attachments");
    const attachments = [];

    // Process each file and convert to buffer
    for (const file of fileAttachments) {
      const bytes = await file.arrayBuffer();
      const buffer = Buffer.from(bytes);

      attachments.push({
        filename: file.name,
        content: buffer,
        contentType: file.type,
      });
    }

    // Set up Nodemailer transporter with Mailtrap SMTP credentials
    const transporter = nodemailer.createTransport({
      host: "live.smtp.mailtrap.io", // Mailtrap SMTP host
      port: 587, // Mailtrap SMTP port
      auth: {
        user: process.env.MAILTRAP_USER, // SMTP username from .env.local
        pass: process.env.MAILTRAP_PASS, // SMTP password from .env.local
      },
    });

    // Email message options
    const mailOptions = {
      from: `"Contact Form" <noreply@yourdomain.com>`, // sender address
      to: "your-email@example.com", // your email or whoever should get the contact message
      subject: "New Contact Form Submission",
      text: `You have a new message from ${name} (${email}):\n\n${message}`,
      attachments: attachments,
    };

    // Send the email
    await transporter.sendMail(mailOptions);

    return NextResponse.json({ ok: true, status: "Email sent successfully" });
  } catch (error) {
    console.error("Error sending email:", error);
    return NextResponse.json(
      { ok: false, error: error.message },
      { status: 500 }
    );
  }
}

Code breakdown:

And one more thing: the attachments option can take objects with various properties. Nodemailer also allows content (e.g., a Buffer or base64 string) if you already have the file data in memory, and even a cid property for embedding images within HTML emails. 

Send email to multiple recipients

To send an email to multiple recipients, simply provide a comma-separated list of emails or an array of email addresses in the to field, like so:

// ... (transporter setup as above)

const mailOptions = {
    from: `"Contact Form" <noreply@yourdomain.com>`, // sender address
    to: "alice@example.com, bob@example.com, charlie@example.com",
    // You could also use an array, e.g.:
    // to: ["alice@example.com", "bob@example.com", "charlie@example.com"],
    subject: "New Contact Form Submission",
    text: `You have a new message from ${name} (${email}):\n\n${message}`,
    // You can add cc or bcc if needed, e.g.:
    // cc: "customer-support@example.com",
    // bcc: "admin@example.com",
};

await transporter.sendMail(mailOptions);

In the above example, the to field includes three email addresses separated by commas. This means the email will be sent to all three recipients..

Tip: You can also use cc to send a copy of the email to others so that all recipients can see who is included. There’s also bcc for blind copies, which is useful if you don’t want your recipients to see each other.

Send email using email API

If you want to automate and speed things up a bit, you can send emails server-side in Next.js via Mailtrap’s very own Node.js SDK that interacts with email API for sending emails.

First, install the Mailtrap Node.js client SDK:

npm install mailtrap

Before we write any code, let’s update the .env.local file to include our Mailtrap API credentials. You can get the Mailtrap API token from Sending Domains > Integration > API section.

# EmailJS credentials
NEXT_PUBLIC_EMAILJS_PUBLIC_KEY=your_emailjs_public_key
NEXT_PUBLIC_EMAILJS_SERVICE_ID=your_service_id
NEXT_PUBLIC_EMAILJS_TEMPLATE_ID=your_template_id

# Mailtrap SMTP credentials
MAILTRAP_USER=api
MAILTRAP_PASS=your_mailtrap_smtp_password

# Mailtrap API credentials
MAILTRAP_TOKEN=your_mailtrap_token

Let’s create a new Next.js API route for sending email with Mailtrap API. Create a file app/api/contact/mailtrap/route.js in your project and paste the following code snippet:

// app/api/contact/mailtrap/route.js
import { NextResponse } from "next/server";
import { MailtrapClient } from "mailtrap";

const mailtrap = new MailtrapClient({ token: process.env.MAILTRAP_TOKEN });

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

    // Send email using Mailtrap
    const result = await mailtrap.send({
      from: { email: "noreply@yourdomain.com", name: "Contact Form" },
      to: [{ email: "your-email@example.com" }],
      subject: "New Contact Form Submission",
      text: `You have a new message from ${name} (${email}):\n\n${message}`,
    });

    console.log("Email sent successfully:", result);

    // Return success response
    return NextResponse.json({ ok: true, status: "Email sent successfully" });
  } catch (error) {
    // Return error response
    console.error("Error sending email:", error);
    return NextResponse.json(
      { ok: false, error: error.message },
      { status: 500 }
    );
  }
}

Now, your Next.js backend can send an email via Mailtrap API whenever this /api/contact/mailtrap endpoint is hit with a POST request. 

Using the API route

To trigger this from the client side, you need to make a POST request to /api/contact with the appropriate data. 

For example, in your React component, you could change the app/components/ContactForm.js file accordingly to use /api/contact/mailtrap instead of /api/contact, as well as the application/json data format compatible with this new Next.js API route. Check it out:

// Example: ContactForm using Mailtrap API instead of Nodemailer SMTP
"use client";
import { useState } from 'react';

export default function ContactFormServer() {
  const [status, setStatus] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = {
      name: e.target.name.value,
      email: e.target.email.value,
      message: e.target.message.value
    };
    try {
      const res = await fetch('/api/contact/mailtrap', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(formData)
      });
      const result = await res.json();
      if (res.ok && result.ok) {
        setStatus('Your message has been sent!');
        e.target.reset();
      } else {
        throw new Error(result.error || 'Failed to send message');
      }
    } catch (err) {
      console.error(err);
      setStatus('Sorry, there was an error sending your message.');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name: <input type="text" name="name" required/>
      </label><br/>
      <label>
        Your Email: <input type="email" name="email" required/>
      </label><br/>
      <label>
        Message: <textarea name="message" required></textarea>
      </label><br/>
      <button type="submit">Send Message</button>
      {status && <p>{status}</p>}
    </form>
  );
}

How it works: On submit, it posts to our /api/contact/mailtrap route, and then handles the JSON response. If successful, it displays a success message and resets the form; if there’s an error, it shows an error message. 

Send HTML email 

Sending an HTML email through the Mailtrap API is similar to plain text; we just provide an html field instead of (or in addition to) the text field in the app/api/contact/mailtrap/route.js file.

Here’s an example of sending a welcome email with some HTML content:

// ... (MailtrapClient initialization setup as above)

await mailtrap.send({
  from: { email: "noreply@yourdomain.com", name: "MyApp Team" },
  to: [ { email: "your-email@example.com" } ],
  subject: "🎉 Welcome to MyApp!",
  html: `
    <html>
      <body style="font-family: Arial, sans-serif; line-height: 1.5;">
        <h2>Hello and Welcome!</h2>
        <p>Thank you for signing up for <b>MyApp</b>. We're thrilled to have you with us.</p>
        <p>If you have any questions, just reply to this email--we're here to help.</p>
        <p>Cheers,<br>The MyApp Team</p>
      </body>
    </html>
  `
});

How it works: The html field can contain any HTML content as a string. You can construct it manually as shown, or load it from an HTML file or template. When the email is sent, the recipient will see the formatted HTML content.

Send email with attachment

To send attachments, simply add the attachments option in the mailOptions object:

// Example: ContactForm using Mailtrap API
"use client";
import { useState } from "react";

export default function ContactFormServer() {
  const [status, setStatus] = useState(null);

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

    const formData = new FormData();
    formData.append("name", e.target.name.value);
    formData.append("email", e.target.email.value);
    formData.append("message", e.target.message.value);

    // Handle multiple file attachments properly
    [...e.target.attachments.files].forEach((file) =>
      formData.append("attachments", file)
    );

    try {
      const res = await fetch("/api/contact/mailtrap", {
        method: "POST",
        // Don't set Content-Type when using FormData - browser will set it automatically
        // with the correct boundary parameter
        body: formData,
      });
      const result = await res.json();
      if (res.ok && result.ok) {
        setStatus("Your message has been sent!");
        e.target.reset();
      } else {
        throw new Error(result.error || "Failed to send message");
      }
    } catch (err) {
      console.error(err);
      setStatus("Sorry, there was an error sending your message.");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name: <input type="text" name="name" required />
      </label>
      <br />
      <label>
        Your Email: <input type="email" name="email" required />
      </label>
      <br />
      <label>
        Message: <textarea name="message" required></textarea>
      </label>
      <br />
      <label>
        Attachments: <input type="file" name="attachments" multiple />
      </label>
      <br />
      <button type="submit">Send Message</button>
      {status && <p>{status}</p>}
    </form>
  );
}

In the app/api/contact/mailtrap/route.js, modify the mailOptions object to include the new attachments property with the attachment data details in it, as well as add the new logic to parse out the file attachments from the multipart/form-data object. Like so:

// app/api/contact/mailtrap/route.js
import { NextResponse } from "next/server";
import { MailtrapClient } from "mailtrap";

const mailtrap = new MailtrapClient({ token: process.env.MAILTRAP_TOKEN });

const toBase64 = async (file) => {
  const bytes = await file.arrayBuffer();
  const base64Content = Buffer.from(bytes).toString("base64");
  return base64Content;
};

export async function POST(request) {
  try {
    // Get form data
    const formData = await request.formData();

    const name = formData.get("name");
    const email = formData.get("email");
    const message = formData.get("message");

    const formAttachments = formData.getAll("attachments");
    const attachments = await Promise.all(
      formAttachments.map(async (attachment) => {
        const content = await toBase64(attachment);
        return {
          filename: attachment.name,
          disposition: "attachment",
          content,
        };
      })
    );

    // Send email using Mailtrap
    const result = await mailtrap.send({
      from: { email: "noreply@yourdomain.com", name: "Contact Form" },
      to: [{ email: "your-email@example.com" }],
      subject: "🎉 Welcome to MyApp!",
      text: `You have a new message from ${name} (${email}):\n\n${message}`,
      attachments,
    });

    console.log("Email sent successfully:", result);

    // Return success response
    return NextResponse.json({ ok: true, status: "Email sent successfully" });
  } catch (error) {
    // Return error response
    console.error("Error sending email:", error);
    return NextResponse.json(
      { ok: false, error: error.message },
      { status: 500 }
    );
  }
}

Send email to multiple recipients

To send an email to multiple recipients using the Mailtrap SDK, you can add multiple objects in the to array. This is straightforward and similar to how we handled it with Nodemailer, except here we must use the array (since the Mailtrap SDK expects to to be an array of recipient objects).

In the app/api/contact/mailtrap/route.js, modify the mailOptions object to include the new to property with the recipients in it:

// ... (MailtrapClient setup)

const result = await mailtrap.send({
  from: { email: "noreply@yourdomain.com", name: "Contact Form" },
  to: [
    { email: "alice@example.com" },
    { email: "bob@example.com" },
    { email: "charlie@example.com" },
  ],
  subject: "🎉 Welcome to MyApp!",
  text: `You have a new message from ${name} (${email}):\n\n${message}`,
});

Test emails and email sending

Now, before moving to production, I strongly advise you to test your emails and sending workflow. Otherwise, you risk having your recipients receive improperly formatted emails, hitting spam filters, getting blacklisted, and more.

So, how do you test your emails without filling your personal inbox or spamming your recipients?

For this, I recommend Mailtrap Email Testing, an inseparable part of the Mailtrap Email Delivery Platfor

Email Testing provides a safe environment to inspect and debug emails in staging, dev, and QA environments. On top of this, it also offers a plethora of other features that allow you to:

With that said, let me show you how easy to set up and use Mailtrap Email Testing is!

SMTP

Create a free Mailtrap account and then:

# EmailJS credentials
NEXT_PUBLIC_EMAILJS_PUBLIC_KEY=your_emailjs_public_key
NEXT_PUBLIC_EMAILJS_SERVICE_ID=your_service_id
NEXT_PUBLIC_EMAILJS_TEMPLATE_ID=your_template_id

# Mailtrap SMTP credentials (Should be testing credentials after updating)
MAILTRAP_USER=your_mailrap_testing_smtp_username
MAILTRAP_PASS=your_mailtrap_testing_smtp_password

# Mailtrap API credentials
MAILTRAP_TOKEN=your_mailtrap_token
// Set up Nodemailer transporter with Mailtrap SMTP credentials
const transporter = nodemailer.createTransport({
  host: "sandbox.smtp.mailtrap.io", // Mailtrap Testing SMTP host
  port: 2525, // Mailtrap Testing SMTP port
  auth: {
    user: process.env.MAILTRAP_USER, // SMTP username from .env.local
    pass: process.env.MAILTRAP_PASS, // SMTP password from .env.local
  },
});

From now on, whenever you trigger your Next.js API Route either manually or by submitting a form, an email will be sent only to your Mailtrap testing inbox.

API

# EmailJS credentials
NEXT_PUBLIC_EMAILJS_PUBLIC_KEY=your_emailjs_public_key
NEXT_PUBLIC_EMAILJS_SERVICE_ID=your_service_id
NEXT_PUBLIC_EMAILJS_TEMPLATE_ID=your_template_id

# Mailtrap SMTP credentials (Should be testing credentials after updating)
MAILTRAP_USER=your_mailrap_testing_smtp_username
MAILTRAP_PASS=your_mailtrap_testing_smtp_password

# Mailtrap API credentials (Should be testing token after updating)
MAILTRAP_TOKEN=your_mailtrap_testing_token
const mailtrap = new MailtrapClient({
  token: process.env.MAILTRAP_TOKEN,
  testInboxId: process.env.TEST_INBOX_ID,
  accountId: process.env.ACCOUNT_ID,
  sandbox: true,
});

Wrapping up

And with that, we’ve come to the end of our article!

Feel free to use it as your go-to guide for setting up a new project, adding sendingfunctionality, and, last but not least, testing your emails! 

If you feel like expanding your JavaScript knowledge, be sure to check out our blog, where you can find other related articles, such as:

Exit mobile version