Create A Newsletter with Next.js API Routes and Mailchimp LR Lee Robinson / November 10, 2019 5 min read • ––– views

The year is 2030. Everyone has a newsletter. Email has overtaken social media. 95% of coding is done via drag-and-drop tools.

Okay, most of that probably isn't true. Except the no-code tools. Unless you've been living under a rock, you've probably noticed the rise of newsletters (especially in the developer community).

Why? Well, they're an excellent way to promote content to those who really want to read it. If you've ever thought about starting a newsletter, then you've found the right article 🎉

Mailchimp Vs. The World #

If you're just starting a newsletter, you probably want something with a free tier. That was my rationale for adopting TinyLetter. It was simple, easy to set up, and didn't require an API route. Perfect!

However, it wasn't exactly a frictionless sign-up process. When a user clicked "subscribe", it launched a pop-up window where they had to confirm their email address again. Again, it works, but we can do better.

I started to explore using Mailchimp as an alternative. It also has a free tier if you have less than 2,000 subscribers. Perfect.

Next.js is the easiest way to build applications in React. One of my favorite features is API routes.

API routes provide a straightforward solution for building an API inside Next.js. All you need to get started is an api/ folder inside your main pages/ folder where your routes live.

Every file inside pages/api/ is mapped to /api/* . This is where we'll communicate with Mailchimp. First, we need to set up an account.

Setting up Mailchimp #

After creating an account, you'll need to fetch your API key. You can utilize their API Playground to test requests.

When a user clicks "subscribe", we'll want to add their email address to the mailing list. This is through the List Members POST endpoint.

According to their API documentation, we'll need to send a request to:

https:// < dc > .api.mailchimp.com/3.0

Where dc is the datacenter. This is the last 3 characters of your API key we retrieved earlier. We'll also need to fetch the Audience ID (or List ID) for the mailing list.

Let's make our secrets we've retrieved available without hardcoding them into our request. Since I'm deploying with Vercel, I'll need to set up some environment variables. You can use similar a similar approach for any framework to add your secrets to process.env .

$ vc secret add MAILCHIMP_LIST_ID < secret-value > $ vc secret add MAILCHIMP_API_KEY < secret-value >

Then, we can expose them via our vercel.json file.

vercel.json

{ "env" : { "MAILCHIMP_LIST_ID" : "@mailchimp-list-id" , "MAILCHIMP_API_KEY" : "@mailchimp-api-key" } }

These variables will be available at build time. To test locally, we'll also want to create a .env file since Vercel Secrets aren't available locally running vc dev .

.env

MAILCHIMP_LIST_ID = < secret-value > MAILCHIMP_API_KEY = < secret-value >

Note: Don't forget to add .env to your .gitignore . We don't want to commit our secrets.

Creating the Request #

Now that we have the API Key and the List ID available as environment variables, we can create an API route at pages/api/subscribe.js to add a member to our list.

pages/api/subscribe.js

export default async ( req , res ) => { const { email } = req . body ; if ( ! email ) { return res . status ( 400 ) . json ( { error : 'Email is required' } ) ; } try { const LIST_ID = process . env . MAILCHIMP_LIST_ID ; const API_KEY = process . env . MAILCHIMP_API_KEY ; const DATACENTER = API_KEY . split ( '-' ) [ 1 ] ; const data = { email_address : email , status : 'subscribed' } ; const response = await fetch ( ` https:// ${ DATACENTER } .api.mailchimp.com/3.0/lists/ ${ LIST_ID } /members ` , { body : JSON . stringify ( data ) , headers : { Authorization : ` apikey ${ API_KEY } ` , 'Content-Type' : 'application/json' } , method : 'POST' } ) ; if ( response . status >= 400 ) { return res . status ( 400 ) . json ( { error : ` There was an error subscribing to the newsletter. Shoot me an email at [me@leerob.io] and I'll add you to the list. ` } ) ; } return res . status ( 201 ) . json ( { error : '' } ) ; } catch ( error ) { return res . status ( 500 ) . json ( { error : error . message || error . toString ( ) } ) ; } } ;

Creating a Form Input #

Now that our API is created, we need a way to gather user input. Let's create a component to send a request to our API.

components/Subscribe.js

import React , { useRef , useState } from 'react' ; function Subscribe ( ) { const inputEl = useRef ( null ) ; const [ message , setMessage ] = useState ( '' ) ; const subscribe = async ( e ) => { e . preventDefault ( ) ; const res = await fetch ( '/api/subscribe' , { body : JSON . stringify ( { email : inputEl . current . value } ) , headers : { 'Content-Type' : 'application/json' } , method : 'POST' } ) ; const { error } = await res . json ( ) ; if ( error ) { setMessage ( error ) ; return ; } inputEl . current . value = '' ; setMessage ( 'Success! 🎉 You are now subscribed to the newsletter.' ) ; } ; return ( < form onSubmit = { subscribe } > < label htmlFor = "email-input" > { 'Email Address' } < / label > < input id = "email-input" name = "email" placeholder = "you@awesome.com" ref = { inputEl } type = "email" / > < div > { message ? message : ` I'll only send emails when new content is posted. No spam. ` } < / div > < button type = "submit" > { '✨ Subscribe 💌' } < / button > < / form > ) ; }

If you'd like to see a completed example, the entire source code for my blog is open source.

Curious if it actually works? Try out the form below 😉

React 2025 Build and deploy a modern Jamstack application using the most popular open-source software.