back button Back to blog

Build a Mailchimp Subscribe Form in Next.js

Create and embed a simple Mailchimp subscription form on your Next.js site to build your audience list.

Noah MatsellJanuary 3, 2023
Copy URL
Contents

In this guide, I'l show you how to create a simple Mailchimp subscribe form component that will look something like this:

A Mailchimp subscribe form

Along with a Next.js api route, this form can be used to add subscribers to your Mailchimp audience lists. This solution will also allow you to customize success and error states.

Best of all, this component can be embedded and reused so you can collect email subscribers anywhere/everywhere throughout your app.

1. Get MailChimp Credentials

  • Get the following credentials from Mailchimp account:
    • API_KEY can be found, or created, in your Mailchimp account Extras > API Keys
    • AUDIENCE_ID can be found from your Mailchimp audience dashboard under Manage Audience > Settings, and then Audience Name & Defaults. This is the audience to which all of your subscribers will be added!
    • API_SERVER is the specific "data center subdomain" that is used for your Mailchimp account. This can be found in a few places -- but if you have your API key from the step above, it is appeneded to the end of the key (looks like us1, us18, etc.). Other ways to find this are listed in the docs.
  • Create a .env file at the root of your project and create new environment variables:
    MAILCHIMP_API_KEY=<api-key-here>
    MAILCHIMP_API_SERVER=<api-server-here>
    MAILCHIMP_AUDIENCE_ID=<audience-id-here>
    

2. Set Up Your Form

  • Create a form component components/SubscribeForm.tsx. This will contain the subscriber's first name and email fields, and/or any other fields you want to collect:
    // SubscribeForm.tsx
    export const SubscribeForm = () => {
      return (
        <form
        onSubmit={subscribeUser}
        className="grid gap-3 grid-flow-row md:grid-flow-col md:w-fit"
        >
        <label
          className="inline-grid text-sm text-gray-600 leading-[2]"
          htmlFor="FNAME"
        >
          First Name
          <input
            className="bg-blue-50/60 px-2 border-b-2 p-1 border-gray-400 text-gray-900 text-xl rounded-none min-w-[240px] leading-10 focus:outline-none focus:border-blueAccent"
            id="FNAME"
            type="text"
            name="name"
            required
          />
        </label>
    
        <label
          className="inline-grid text-sm text-gray-600 leading-[2]"
          htmlFor="EMAIL"
        >
          Email
          <input
            className="bg-blue-50/60 px-2 border-b-2 p-1 border-gray-400 text-gray-900 text-xl rounded-none min-w-[240px] leading-10 focus:outline-none focus:border-blueAccent"
            id="EMAIL"
            type="email"
            name="email"
            required
          />
        </label>
        <Button
          variant="secondary"
          type="submit"
          className="md:leading-[0.95rem] mt-4 md:mt-0"
        >
          Subscribe
        </Button>
        </form>
      );
    }
    
  • Add types based on the fields in your newly created form. Important: the email and name entries in the FormElements interface correspond with the input name attributes from the form above:
    // SubscribeForm.tsx
    interface FormElements extends HTMLFormControlsCollection {
      email: HTMLInputElement;
      name: HTMLInputElement;
    }
    
    interface SubscribeFormElement extends HTMLFormElement {
      readonly elements: FormElements;
    }
    
  • Add a form submission handler. The subscribeUser function below gets the form field values and POSTs them to the /api/subscribe route, which we will create in a moment:
    // SubscribeForm.tsx
    ...
    export const SubscribeForm = () => {
      const subscribeUser = async (
        e: React.SyntheticEvent<SubscribeFormElement>
      ) => {
        e.preventDefault();
    
        const res = await fetch("/api/subscribe", {
          body: JSON.stringify({
            email: e.currentTarget.elements.email,
            name: e.currentTarget.elements.name
          }),
          headers: {
            "Content-Type": "application/json",
          },
          method: "POST",
        });
    
        if (res.status === 201) {
          // Custom behaviour on success (like a success message)
          console.log("success");
        } else {
          // Custom behaviour on error (like an error message)
          console.log("error");
        }
      };
    ...
    
    • Optionally, replace the success and error console logs with your own custom behaviour. This could include showing a message, a banner, a toast, and much more.
    • Note: The form takes advantage of native HTML form validation, so this function triggers only on valid submissions.

[Full SubscribeForm.tsx code]

3. Set Up API Route

  • Install the @mailchimp/mailchimp_marketing module via yarn or npm.
  • Create a file pages/api/subscribe.ts
  • Set up mailchimp client:
    // subscribe.ts
    import type { NextApiRequest, NextApiResponse } from "next";
    const client = require("@mailchimp/mailchimp_marketing");
    
    client.setConfig({
      apiKey: process.env.MAILCHIMP_API_KEY,
      server: process.env.MAILCHIMP_API_SERVER,
    });
    ...
    
  • Create the handler for this api route:
    ...
    export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse
    ) {
      const { email, name } = req.body;
    
      if (!email || !name) {
        return res.status(400).json({ error: "Email and name are required" });
      }
    
      try {
        const response = await client.lists.addListMember(
          process.env.MAILCHIMP_AUDIENCE_ID,
          {
            email_address: email,
            merge_fields: {
              FNAME: name,
            },
            status: "subscribed",
            tags: ["portfolio"],
          }
        );
    
        if (response.status >= 400) {
          return res.status(400).json({
            error: `There was an error subscribing to the newsletter.`,
          });
        }
    
        return res.status(201).json({ error: "" });
      } catch (error) {
        return res
          .status(500)
          .json({ error: (error as Error).message || (error as Error).toString() });
      }
    }
    
    • This handler will create a new audience member with the submitted first name (FNAME), email address. The handler also sets the member as "subscribed" status and tagged with "portfolio"

[Full subscribe.ts code]

More

You can capture more fields and merge fields as outlined by Mailchimp's Marketing API and Merge Fields docs. If you do add/change fields, be sure to make the corresponding updates in both the SubscribeForm.tsx and subscribe.ts files.

Last Thougts

You now have a small, lightweight subscribe form component that can be embedded anywhere in your app. Add it to your "about" page, at the end of your blog posts, on your personal portfolio page, in a banner.


Like this post?

Sign up and get notified when new posts are published!



Comments