How to create a free email subscription form in NextJS, Netlify and MailerLite
Tags: tutorial, nextjs, netlify, webdev
Published: 2023-03-08 21:53:42

For the past months I have thought multiple times about starting my own blog and creating content for social media. Something which always bothered me was the fact that I did not know how to create an emailing list even though I knew having one is very valuable! I will share with you the end result of what I am currently using on my blog, [duncanteege.com](duncanteege.com)



## Important money saving tip
There are many ways to go about creating an email subscription list using a form. One of those ways is by using the functionality of writing your own endpoints in NextJS and using that to communicate to an email service like MailerLite. The downside is that this will make use of Node because of you defining an API route. This in itself definitely is not a downside, however having to host this might be a potential downside as it could bring extra costs. I first chose to go this route and hosted my website on DigitalOcean on an app platform. They gave me estimated costs of $5 a month just for hosting a static website with an endpoint! I thought “Nah, gotta be a different way right?”... and there is!


## Netlify Forms
It’s free! Netlify proudly presents the Netlify Forms functionality. They allow you up to 100 form submissions per site per month in their free “starter” tier. This is very generous and this is one of the many things why I love Netlify. (I am not sponsored, just amazed at what they offer 🙂) It must be hard to implement the form if it is free… right?

Nope. It is ridiculously easy. All you have to do is add a simple attribute `netlify` to your form. Yes it is that simple! What happens is that Netlify bots find the attribute by inspecting the HTML of your site before it is published. This means there is no other API call needed! Something to note is that using Netlify Forms means you have to host your website on Netlify.


## What does the form look like in NextJS
Let’s get dirty with some code. 

```tsx
<form
  name="subscribe"
  method="POST"
  data-netlify="true"
  onSubmit={handleSubmit}
>
  <div className={`${styles.formContainer}`}>
    <div className={`${styles.inputGroup}`}>
      <label htmlFor="name"></label>
      <input
        type="text"
        id="name"
        name="name"
        value={subscriberName}
        required
        placeholder="What is your first name?"
        className={`${styles.input}`}
        onChange={(e) => setSubscriberName(e.target.value)}
      />
    </div>
    <div className={`${styles.inputContainer}`}>
      <label htmlFor="email"></label>
      <input
        type="email"
        id="email"
        name="email"
        required
        value={subscriberEmail}
        placeholder="On what email can I reach you?"
        className={`${styles.input}`}
        onChange={(e) => setSubscriberEmail(e.target.value)}
      />
    </div>
    <SubmitButton text="Subscribe" />
  </div>
</form>
```

This is the form I use on my blog duncanteege.com. Note how there’s just a simple data attribute to notify Netlify bots that I want to make use of Netlify Forms? Besides that, the form just looks like a normal HTML form in React.


## Do not reload or navigate to a new page
Standard behaviour of using Netlify Forms is that it navigates you to a different page when submitting. I did not want this on my website. I wanted users to submit and then see the subscribe block change, but not a whole page refresh. Luckily they had a very nice example in their documentation where they explain how to use `fetch` to POST to the website so you could change based on the response of the request. This code looks like this:

```tsx
const handleSubmit = async (e: any) => {
    e.preventDefault();

    const form = e.target;
    const formData = new FormData(form);
    try {
      await fetch("/", {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams(formData as any).toString(),
      });
      setFormState(FormState.SUCCESS);
    } catch (error) {
      setFormState(FormState.ERROR);
      console.error(error);
    }
  };
```

It is not super exciting, but this cancels the page refresh and enables me to act on state changes based on the response of the request! This code runs on the `onSubmit` of my form. I handle the form status in my own state so I can act upon changes of this state.


## What to do in MailerLite
There are many email services out there. I did some research and it looked like MailerLite was the best option for me. It offered, just like Netlify, a very generous free tier. It is free for up to 1000 subscribers. Next to this, you can send up to 12.000 emails a month in the free tier! That’s… like 12.000 emails a month! Holy smokes.

Before we look at how to hook it all together, let us prepare the necessary keys we need to create a successful request to our email service. I will show you what you need to do in MailerLite, but feel free to skip the next paragraph if you do not use MailerLite.

In MailerLite there is a menu item called “Integrations”. When you click on this item, you will see a page with possible integrations you could use with MailerLite. Quite an impressive list, but we’re interested in the “API” functionality as we want to call the API from our Netlify functions. More about Netlify functions later. Click on “use” and generate an API token. Make sure to save this token (but don’t commit it to your repo, store it in your .env file). Something you should also take note of is the header they want you to send:

```
Content-Type: application/json
Accept: application/json
```
                
And the base url is `https://connect.mailerlite.com/`

Cool, but how do we actually make use of this api? This is where Netlify Functions comes in clutch.


## Using Netlify Functions
So we use Netlify Forms to submit our forms. Our submits end up in Netlify form submissions. We could manually type these submissions into MailerLite… but we’re developers right? Manual work is not for us. Luckily Netlify also offers Netlify Functions! Again Netlify is generous by offering 125.000 invocations per month in the “starter” tier. I mean… what? That’s an insane amount. But how do we use it with our form submissions?

This sounds like we should listen to some kind of event or trigger. If we look at the documentation, it states that there are a couple of events we could listen to… including `submission-created`. Hold up! That’s what we need! So how do we use this?

Netlify wants you to create a specific folder for serverless functions. This folder lives in the root level of your project, the same level as `src` if you are also doing this in NextJS. This folder is called `netlify`. In this folder you create another folder called `functions`. This is where your serverless functions will live. In order to listen to the `submission-created` event, we create a file called `submission-created.ts` (to use TypeScript) in the `functions` folder. In this folder they expect an async function called `handler`. In this function you can get the body of the POST request from the form and handle it the way you need it to handle. This is what the handler could look like.

```javascript
const axios = require("axios");

const API_KEY = process.env.MAILERLITE_PRODUCTION_API_KEY;
const BASE_URL = process.env.MAILERLITE_PRODUCTION_BASE_API_URL;
const GROUP_ID = process.env.MAILERLITE_PRODUCTION_NEWSLETTER_GROUP_ID;


async function handler(event: any) {
  if (!event.body) {
    return;
  }
  const { email, name } = JSON.parse(event.body).payload;
  const url = `${BASE_URL}/subscribers`;


  const data = {
    email: email,
    fields: {
      name: name,
    },
    groups: [`${GROUP_ID}`],
  };


  const options = {
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
    },
  };


  try {
    await axios.post(url, data, options);
    return {
      statusCode: 201,
      body: JSON.stringify({
        message: "Subscriber successfully created and added to group",
      }),
    };
  } catch (error: any) {
    console.log(error);
    return {
      statusCode: 500,
      body: JSON.stringify(error),
    };
  }
}

export { handler };
```

Note: as you can see I make use of environment variables. These environment variables should be added to your website in Netlify to make sure that the handler works. You can use environment variables in a .env file. They are defined like this:

```
MAILERLITE_PRODUCTION_API_KEY = “your-api-key-here”
MAILERLITE_PRODUCTION_BASE_API_URL = https://connect.mailerlite.com/api
MAILERLITE_PRODUCTION_NEWSLETTER_GROUP_ID = “your-group-id-here”
```


In the code snippet above of the handler you can also see I create a data object with all the data I want to send to MailerLite. Since I also directly want to add the new subscribers to a specific newsletter group, I add that to my data object as well. It is important to remember that in order for the serverless function to work, the project actually needs to be deployed as well.


## Summary
This turned out to be quite a long blog post, but all in all it is relatively easy to set up. There are a couple of steps you need to take in order to successfully create your own form without paying for a Node server with an API route if you are just starting out. Netlify offers a lot of features and we made use of the very generous starter tier of their plans! Oh and… I’m not sponsored by Netlify or MailerLite. Just a big fan of their services.

If this post helped you and you want more of these posts, consider subscribing to [my newsletter](https://duncanteege.com/blog/how-to-create-an-email-subscription-form-in-nextjs-netlify-and-mailerlite#newsletter)! I'll share quality content once a month, no spam.