A Beginner's Guide to Integrating Next.js and Mailtrap for Seamless Email Testing
Tags: nextjs, react, mailtrap
Published: 2023-03-09 01:22:08

by [Divine Orji](https://blog.openreplay.com/authors/divine-orji)

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](https://github.com/dpkreativ/cool-user-feedback).

## Prerequisites

You will need the following:

- Knowledge of JavaScript and React
- A GitHub account. Create one [here](https://github.com)
- [Yarn](https://classic.yarnpkg.com/en/docs/install) installed on your PC. You need to install [Node](https://nodejs.org) 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](https://github.com/dpkreativ/feedback-form-template/generate) to generate your repo based on a feedback form template built in Next.js:

![Generate template](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/U4z94ZN.png)

### Clone your repo and install dependencies

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

```bash
# 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:

```bash
+---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:

```javascript
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:

```javascript
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](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/oR1qCaN.png)

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

```javascript
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:

```javascript
<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](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/Ss2Lvdl.png)

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

```javascript
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`:

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


<h2>Session Replay for Developers</h2> 
<p><em>
Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data
</em></p>
<img alt="OpenReplay" width="768" height="400" src="https://blog.openreplay.com/assets/overview_Z17Qdyg.png" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em>





## Setting Up Mailtrap

In your browser, navigate to [mailtrap.io](http://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](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/GhrE0ST.jpg)

On your dashboard's sidebar, click on "Email Testing" and select the default inbox or create your custom inbox:

![Email testing on Mailtrap](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/nz8n6YI.jpg)

Click on your inbox's name to open it. Under "Integrations", select "Nodemailer":

![SMTP Settings for Nodemailer](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/Ia8ZvwK.jpg)

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`:

```bash
yarn add nodemailer
```

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

```javascript
// 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:

```javascript
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](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/BPZtNjx.gif)

## Testing and Debugging

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

![Viewing feedback on Mailtrap](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/X86acGM.jpg)

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](https://blog.openreplay.com/images/integrating-nextjs-and-mailtrap-for-seamless-email-testing/images/03WkkqU.jpg)

## 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](https://blog.openreplay.com/).



[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)