Back

A Beginner's Guide to Integrating Next.js and Mailtrap for Seamless Email Testing

A Beginner's Guide to Integrating Next.js and Mailtrap for Seamless Email Testing

For apps that use emails to contact users or send notifications, email testing is a crucial step in the development process. It helps to ensure that your app’s email functionality works as expected and that you send the appropriate content to the correct recipients.

One tool that can be particularly useful for email testing is Mailtrap: a simulated environment that allows you to test email functionality without actually sending any emails to real users. This reduces the risk of sending emails with sensitive information or to unintended recipients.

This article will teach you how to set up Mailtrap via Next.js’ API route. You will also preview email responses on Mailtrap’s dashboard to ensure your app sends them correctly.

You can look at its source code here.

Prerequisites

You will need the following:

  • Knowledge of JavaScript and React
  • A GitHub account. Create one here
  • Yarn installed on your PC. You need to install Node first, which allows you to install Yarn via npm
  • Knowledge of Next.js is preferable but not strictly required

Setting Up Your Next.js Project

Generate starter files from a template

To get started, click here to generate your repo based on a feedback form template built in Next.js:

Generate template

Clone your repo and install dependencies

In your PC’s terminal, navigate to your preferred directory and run the commands below:

# clone your newly-generated repo
git clone repo-git-url

# cd into your cloned repo's directory
cd repo-directory

# install dependencies
yarn

In the code above, you did the following:

  1. Cloned your newly-generated repo to your PC
  2. Navigated into your repo’s root directory
  3. Installed your project’s dependencies using yarn. If you prefer to use npm, run npm install instead of yarn

View project structure

Open your newly-cloned project in your preferred code editor, and examine its files and folders:

+---components
|   +---layouts
|   |       HomeLayout.js
|   |
|   \---ui
|           FeedbackForm.js
|           ThankYouMessage.js
|
+---pages
|   |   index.js
|   |   _app.js
|   |   _document.js
|   |
|   \---api
|           mailtrap.js

In the components/ui folder, you have your FeedbackForm component and a ThankYouMessage that will display after your user has sent a message.

You will import these components into your pages/index.js file and create a serverless function in pages/api/mailtrap.js to handle your Mailtrap backend and provide an API route for your app.

Set up and manage your app’s state

Open pages/_app.js and update it with the code below:

import '@/styles/globals.css';
import { createContext, useState } from 'react';

export const AppContext = createContext();

export default function App({ Component, pageProps }) {
  const [submitted, setSubmitted] = useState(false);

  return (
    <AppContext.Provider value={{ submitted, setSubmitted }}>
      <Component {...pageProps} />
    </AppContext.Provider>
  );
}

In the code above, you created a variable AppContext that utilizes createContext() from React’s Context API.

With React’s useState() hook, you created a default state for your form’s submit status.

Finally, you wrapped your <Component /> with <AppContext.Provider>, giving your app global access to your state.

To see it in action, navigate to your pages/index.js file, and update the default Home function with the code below:

export default function Home() {
  const { submitted } = useContext(AppContext);

  return (
    <>
      <Head>
        <title>Feedback Form</title>
        <meta name="description" content="Feedback Form" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <HomeLayout>
        <section className="md:rounded-3xl md:shadow-lg bg-[#fcfcfc37] overflow-hidden max-w-7xl mx-auto">
          {submitted ? <ThankYouMessage /> : <FeedbackForm />}
        </section>
      </HomeLayout>
    </>
  );
}

Here, you accessed submitted from your global state via useContext(AppContext). Be sure to import AppContext from _app.js and useContext from React.

In your <HomeLayout> component, you set the <section> tag to display a <ThankYouMessaage /> when submitted is true and <FeedbackForm> when it is false.

By default, submitted is false, so when you run yarn dev in your project’s terminal and open localhost:3000 on your browser, you will see the UI below:

Default UI

Open your components/ui/FeedbackForm.js file and update the FeedbackForm function with the code below:

export default function FeedbackForm() {
  // begin
  const { setSubmitted } = useContext(AppContext);

  const [data, setData] = useState({
    name: ",
    feedback: ",
  });

  function handleChange(e) {
    setData({ ...data, [e.target.name]: e.target.value });
  }

  async function handleSubmit(e) {
    e.preventDefault();
    console.log(data);
    setSubmitted(true);
  }
  // end - do not change any code below this line yet

  return (
    // html code for Feedback form - do not update the code here yet
  );
}

You accessed setSubmitted from the global state and created a local component state that holds the default values for your form’s name and feedback data.

The handleChange function updates your data’s state as a user changes the form’s input.

The handleSubmit function displays the user’s input in your console and sets your global submitted state to true.

Scroll down to the <form> tag and update it with the code below:

<form
  onSubmit={handleSubmit}
  onChange={handleChange}
  className="grid gap-16"
>

When a user types in their name or feedback, onChange will trigger the handleChange function, storing the input value to your data state in real time.

When a user submits the form, onSubmit will trigger the handleSubmit function. If you verify it in your browser, you will see something similar to this:

Thank you page with form data

Navigate to components/ui/ThankYouMesage.js and update the ThankYouMessage function with the code below:

export default function ThankYouMessage() {
  // begin
  const { setSubmitted } = useContext(AppContext);

  function handleClick() {
    setSubmitted(false);
  }
  // end - do not change any code below this line yet

  return (
    // html code for Feedback form - do not update the code here yet
  );
}

Here you accessed setSubmitted from the global state and created a handleClick function that sets submitted to false.

Scroll down to the <button> tag and add an onClick event to trigger handleClick:

<button
  onClick={handleClick}
  className="rounded-2xl px-4 py-3 bg-[#434058] text-[#FCFCFC]"
>

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

Setting Up Mailtrap

In your browser, navigate to mailtrap.io and click on “Sign up” to create an account and access your dashboard (if you already have a Mailtrap account, click on “Login” instead):

Mailtrap dashboard

On your dashboard’s sidebar, click on “Email Testing” and select the default inbox or create your custom inbox:

Email testing on Mailtrap

Click on your inbox’s name to open it. Under “Integrations”, select “Nodemailer”:

SMTP Settings for Nodemailer

When the integration code for Nodemailer displays, copy your auth.user and auth.pass details, create a .env.local file in your project’s root directory, and paste the auth details into it:

MAILTRAP_USER=mailtrap-nodemailer-auth-user
MAILTRAP_PASS=mailtrap-nodemailer-auth-pass

Integrating Next.js And Mailtrap

In your project’s terminal, run the code below to install nodemailer:

yarn add nodemailer

After successfully installing Nodemailer, open your pages/api/mailtrap.js file and update it with the code below:

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import nodemailer from 'nodemailer';

export default function handler(req, res) {
  const body = req.body;

  const mailData = {
    from: 'test@example.com',
    to: 'test@example.com',
    subject: `New feedback from ${body.name}`,
    text: `${body.feedback}`,
    html: `<p>${body.feedback}</p>`,
  };

  const transport = nodemailer.createTransport({
    host: 'sandbox.smtp.mailtrap.io',
    port: 2525,
    auth: {
      user: process.env.MAILTRAP_USER,
      pass: process.env.MAILTRAP_PASS,
    },
  });

  transport.sendMail(mailData, (error, info) => {
    if (error) console.log(error);
    console.log(`Message sent: ${info.messageId}`);
  });

  res.status(200).json({ body });
}

Here you did the following:

  • Imported nodemailer and exported a default handler function that contains the logic for sending your form’s feedback to Mailtrap
  • In your handler function, you created a body variable that takes in the body of your client’s request
  • You created a mailData variable that contains your email’s details such as its sender, recipient, subject, and feedback in text and html. Since you’re testing with Mailtrap, the sender and recipient is ”test@example.com
  • You created a transport variable that initializes nodemailer, setting the host URL, port, and auth details. You passed in the auth details stored in your .env.local file through process.env
  • With transport, you called the sendMail() method to send your email details. If it sends successfully, you will see your email’s messageId in your console or an error message if any errors occur
  • Finally, you set the status of your API’s response to 200 indicating that it is successful

In components/ui/FeedbackForm.js, update your handleSubmit function with the code below:

async function handleSubmit(e) {
  e.preventDefault();

  try {
    await fetch('api/mailtrap', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify(data),
    })
      .then((res) => res.json())
      .then(() => setSubmitted(true));
  } catch (error) {
    console.error(error);
  }
}

You used JavaScript’s fetch in the code above to send a POST request containing your form’s data. Your API’s URL - api/mailtrap - is the Next.js API route you created.

You then converted your POST’s successful response to json and set submitted to true, displaying your app’s “Thank You” message in your browser.

See it in action below:

Feedback demo

Testing and Debugging

In your browser, open your Mailtrap dashboard to see your newly-sent feedback message:

Viewing feedback on Mailtrap

You can view your message’s HTML Source code or see it as regular text. Spam Analysis checks if your email has the potential to be flagged as spam, and HTML Check confirms that you can view your email content on different browsers and devices:

HTML Check

Conclusion

This article showed you how to test email messages from your Next.js app with Mailtrap. You also learned how to set up your app’s state globally with React’s Context API and how Next.js API routes work. There are so many exciting possibilities with Next.js and Mailtrap, and you can learn about them here on OpenReplay.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay