Nona Blog

Creating a newsletter with Next.js and Mailchimp


In this article I’m going to show you how to use Next.js and Mailchimp to create an email newsletter.

You can jump straight to the repository to see all the code if you are pressed for time.

Here’s the link below:

Getting started

Next.js is a JavaScript framework which lets you build React apps that can be rendered on the server or be exported to a static bundle.

The API Route feature makes Next.js a great option to use for a task like this. Placing a file in the pages/api/ folder will create an API endpoint.

They are server-side-only bundles and therefore won’t increase your client-side bundle size at all.

For an API route to work, you need to export it as a default function (or request handler), which then receives the parameters req and res. Here’s a simple example from the project (in /pages/api/hello.js).

export default (req, res) => {
  return res.status(200).json({
    message: "Hello world!",

If you go to localhost:3000/api/hello you should see the following in your browser.

Server-side implementation

Let’s dive into the server-side implementation. I created a new file called newsletter.js in pages/api. This becomes a new API route as explained above.

See the function shown below:

export default async (req, res) => {
  const { email } = req.body

  if (!email || !email.length) {
    return res.status(400).json({
      error: "Forgot to add your email?",

  try {
    const { url, data, headers } = getRequestParams(email)

    const response = await, data, { headers })

    // success
    return res.status(201).json({ error: null })
  } catch (error) {
    return res.status(400).json({
      error: `Oops, something went wrong... Send me an email at and I'll manually add you to the list.`,

The code checks the email parameter to verify that an actual email has been submitted.

The try block is where the code attempts to access the Mailchimp API. The getRequestParams function returns the url, data and headers.

I won’t go into detail about this function as the comments explain whats going on:

function getRequestParams(email) {

  // get env variables
  const API_KEY = process.env.MAILCHIMP_API_KEY

  // get the mailchimp datacenter
  // mailchimp API keys example: c0e214879c8542a54e716f38edf1635d-us2
  // we need the us2 part
  const DATACENTER = process.env.MAILCHIMP_API_KEY.split("-")[1]

  const url = `https://${DATACENTER}${AUDIENCE_ID}/members`

  // you can add additional parameters here. 
  // see full list of available parameters at:
  const data = {
    email_address: email,
    status: "subscribed",

  // API key needs to be encoded in base 64 format
  const base64ApiKey =   Buffer.from(`anystring:${API_KEY}`).toString("base64")

  const headers = {
    "Content-Type": "application/json",
    Authorization: `Basic ${base64ApiKey}`,

  return {

The Mailchimp API receives a post request using the parameters returned by getRequestParams.

Heres the line where that takes place:

const response = await, data, { headers })

The line below returns a status 201 and null error if Mailchimp returns no errors:

 return res.status(201).json({ error: null })

The next lines below return a status 400 and the associated error message if Mailchimp does return any error:

return res.status(400).json({
      error: `Oops, something went wrong... Send me an email at                  and I'll manually add you to the list.`,

Client side component

Now let’s discuss the Newsletter component. This component will make the request to subscribe to the newsletter.

There are three state variables. The first being for the email, the second for the state of submission (i.e. idle, loading, success or error) and the last one for the error message which is the error message returned by the server.

const [email, setEmail] = useState("")
const [state, setState] = useState("IDLE")
const [errorMessage, setErrorMessage] = useState(null)

Next we have the actual component. I used css modules for the css styling as this comes built in with Next.js.

 <div className={styles.container}>
      <h2 className={styles.header}>Subscribe to my newsletter!</h2>
      <p className={styles.details}>
        It includes interesting tech content as well as some information on how
        to live life to the fullest!
          placeholder="Enter Email"
          onChange={(e) => setEmail(}
          disabled={state === "LOADING"}
          {state === "LOADING" ? "Loading" : "Subscribe"}
      {state === "ERROR" && <p className={styles.errorMsg}>{errorMessage}</p>}
      {state === "SUCCESS" && <p className={styles.successMsg}>Success!</p>}

Clicking on the button calls the subscribe function below. The function initially sets state to ‘LOADING’ and the error message to null.

The endpoint “/api/newsletter” receives a post request with the email entered in by the user.

If no errors occur then the state value is set to ‘SUCCESS’. If an error does occur then the error message state is set to the error response data and the state value is set to ERROR.

const subscribe = async () => {
    try {
      const response = await"/api/newsletter", { email })
    } catch (e) {

Here’s the final result.

You can find out more about Next.js through their documentation.

Link to the repo

Nona designs and builds intuitive software for FinTech businesses. If you’d like to accelerate your FinTech project, book a consultation with us!

David Fransch

Junior Developer - Nona