Create a Newsletter app with Twitter Revue, Next.js API Routes, and Tailwindcss
Learn how to create a newsletter with Revue APIs, Next.js API routes, and Tailwindcss. The best way to include a Newsletter in your Next.js app/site.
Hey there 👋, do you like to learn from video tutorials? This article is also available as video content.
Please feel free to subscribe for the future content
Do you have an email newsletter, or consider starting one? An email newsletter gives your subscribers regular updates about your work, products, passion, life, journey, anything that you find suitable to share. We have some great vendors/products/sites that help us to instantly get started with an email newsletter service. Buttondown, Mailchimp, MailerLite, Substack are just a few to name here.
Early this year, Twitter announced the acquisition of Revue, a service that makes it free and easy for anyone to start and publish editorial newsletters. Not only that. Twitter has also made Revue’s Pro features free for all accounts.
A few days back, a tweet from Revue's official account confirmed that they would allow people to subscribe to your Revue newsletter directly from your Twitter profile. Keeping some debates aside, I think it is a great move.
As the owner of a newsletter, we can promote it in many ways.
- We can link to the newsletter page from our website, blog.
- We can embed the subscription form to our website using simple JavaScript, HTML, CSS snippets provided by vendors.
- Lastly, if the newsletter vendor provides APIs to access data, we can create, manage the newsletter entirely within our control. It is a powerful usage that gives your users a feeling of
oneness
being part of the same website, similar look-and-feel.
So, What's the Plan?
This tutorial will teach how to use the Revue
APIs to fetch data into a Next.js
application using the API routes(serverless functions). We will also use the tailwindcss
to give the app a better look and feel.
I am on my way to migrate my old website to new website using Next.js
and tailwindcss
, and the newsletter will be a part of it. So, it is an excellent opportunity to share what I have implemented and learned.
TL;DR
If you want to jump into the final app or the source code early, here are the links,
Setup a Newsletter Service using Revue
To set up a newsletter with Revue
, sign up to https://www.getrevue.co/ using your Twitter account or email.
Next, log in to your account to set up the newsletter by providing the name, description, layout, issues, and schedule. You can integrate many services like Twitter, Facebook, Instagram with your Revue account to fetch content from them to add to the newsletter. Additionally, you can fetch the data using the RSS feeds. You can integrate your Hshnode powered blog's RSS feed as well. I've made my wish to Sam Sycamore already 😆!
The bottom of the integration page shows your API key to access the newsletter data over HTTP requests. Please copy this key and keep it safe.
This API key will be part of the Authorization
header value when using the Revue APIs. Here is the link to learn about all publicly available APIs. In this tutorial, we will use the following,
POST /v2/subscribers
: Add a subscriber to the list.GET /v2/subscribers
: Returns a list of your active subscribers.GET /v2/issues
: Returns a list of your sent issues.
But, before that, let us build the user interface of the Newsletter Subscription app.
Build a Newsletter Subscription App using Next.js and Tailwindcss
There are plenty of starter projects available in GitHub to get started with Next.js and Tailwindcss. My personal favorite is next-starter-tailwind because of its simplicity. I'll be using it as a template to create a repository for the newsletter subscription app. Please feel free to use any other starter project you are comfortable with.
Please create a repo by clicking on the Use this template
button of the next-starter-tailwind
repository.
Provide required details and create a repository from the template.
Now clone the repository and browse to the project folder. Open a command prompt or terminal window to install dependencies using the following command,
npm install # Or, yarn install
At this stage, please open the project with your favorite code editor(VS Code, in my case) and make minor code changes. Open the header.js
file under the components
folder and find the Next.js Starter Tailwind
text. Change this text to Newsletter demo powered by Next.js Revue Tailwind
. Additionally, you can change the creator name, GitHub information in the footer.js
file.
Now save your changes and use this command from your command prompt to launch the app.
npm run dev # Or, yarn dev
Access the app using the URL http://localhost:3000
. You should see the initial user interface coming up.
Create the Subscription Form
Let's create a basic subscription form with a single email field and a button to subscribe. Please create a new file called Subscribe.js
under the components
folder with the following content.
const Subscribe = () => {
return (
<div className="border border-gray-200 rounded p-6 my-4 w-full bg-gray-50">
<p className="text-gray-900 mb-6 text-lg md:text-xl">
Want to keep your brain engaged with great UI/UX learning content?
</p>
<p className="text-gray-800 dark:text-gray-400 mb-10 text-base">
Enter your email address and you'll be be added to my email newsletter, of which you can opt out any time.
</p>
<form className="relative my-4">
<input
aria-label="Email for newsletter"
placeholder="john@email.com"
type="email"
autoComplete="email"
required
className="py-4 px-0 text-md bg-transparent w-9/12 text-gray-900 border-b-2 border-gray-600 dark:border-gray-400 dark:text-white focus:border-brand focus-visible:outline-none"
/>
<button
className="flex justify-center px-5 py-4 mt-8 bg-green-600 text-white font-bold text-lg"
type="submit"
>
Subscribe
</button>
</form>
<p className="text-xl text-gray-800 dark:text-gray-200">
14 subscribers . 3 issues
</p>
</div>
);
};
export default Subscribe;
It is a react component with a simple form having one email field and a button. We have also hardcoded the subscribers and issues count. Later, we will make the API calls to fetch those. We have styled the HTML element using tailwindcss classes.
Now move over to the index.js
under the pages
folder. Replace the content of the file with the following,
import Subscribe from "@components/Subscribe";
export default function IndexPage() {
return (
<Subscribe />
);
}
Here we are importing and using the Subscribe
component so that when the app loads, it shows the newsletter subscription form. Let's refresh the page. You should see subscription forms like,
Create Next.js API Routes to Subscribe, and Many More
Now it's time to create Next.js API Routes
to register a new subscriber, get the subscriber count, and list of issues.
Next.js Serverless Functions
With Next.js's API Routes, you can easily create API endpoints. In the background, it uses Node.js serverless functions. You need to create these functions inside the pages/api
folder. So, let us first create a folder called api
under the pages
folder.
We will need the Revue API key now. Please create .env.local
file at the root of the project folder with the following line,
REVUE_API_KEY=<REPLACE_THIS_WITH_REVUE_API_KEY>
Please use your API Key
you have copied from the revue integration page earlier.
At this stage, you need to restart the local server for the environment variable to get loaded in our app. So stop the server and restart it using the yarn dev
command.
Let's create the API route to register a new subscriber.
But, Hold On! Why Can't We use the Revue API Directly?
You can. It is possible to use the Revue APIs directly in your React components. However, there are a few advantages of using it via the Next.js APIs.
- In the future, if you want to use another newsletter service other than Revue, your user interface component code never changes. You just change the serverless function and redeploy.
- There is an abstraction. It helps you to deploy and host just the API separately along with your own business use cases.
- Accessing these APIs directly on the client-side will leave you with the risk of the
API key
exposed that anyone can obtain easily by inspecting network requests. You do not want that!
Alright, let's move on.
Create Next.js API Route to Register a New Subscriber
Create a file called subscribe.js
inside pages/api
folder. It means our API route will be accessible from the UI components using the URI /api/subscribe
. Please paste the following content in the subscribe.js
file.
export default async function handler(req, res) {
// 1. Get the email from the payload and
// validate if it is empty.
const { email } = req.body;
if (!email) {
return res.status(400).json({error: 'Please provide an email id.'});
}
// 2. Use the Revue API Key and create a subscriber using
// the email we pass to the API. Please note, we pass the
// API Key in the 'Authorization' header.
try {
const API_KEY = process.env.REVUE_API_KEY;
const response = await fetch(
`https://www.getrevue.co/api/v2/subscribers`,
{
method: 'POST',
body: JSON.stringify({email: email, double_opt_in: false}),
headers: {
'Authorization': `Token ${API_KEY}`,
'Content-Type': 'application/json'
}
}
)
// 3. We check in the response if the status is 400
// If so, consider it as error and return. Otherwise a 201
// for create
if (response.status >=400) {
const message = await response.json();
console.log(message.error.email[0]);
return res.status(400).json({error: message.error.email[0]});
}
// Send a JSON response
res.status(201).json({
message: `Hey, ${email}, Please check your email and verify it. Can't wait to get you boarded.`,
error: ''
});
} catch (err) {
// 4. If the control goes inside the catch block
// let us consider it as a server error(500)
return res.status(500).json({error: err.message || error.toString()});
}
}
A few things are going on in the above function.
- When someone invokes this API function, we expect an email part of the payload. So first, get the email from the payload and validate if it is empty.
- Next, use the email and API_KEY to call the Revue API to register a subscriber. Note the payload here. We are passing the email value and
double_opt_in
value asfalse
. In reality, you will NOT pass the double_opt_in value as false as you want your subscribers to verify email before confirming. We are doing it just for the demo's sake. - Then, we check in the
response
if the status is 400. If so, consider it as an error and return with an error message. Otherwise, a 201 for creating and return with a success message. - Last, If the control goes inside the catch block, let us consider it a server error(500).
Update the UI Code to Register Subscribers
We will update the Subscribe
component to use the /api/subscribe
API. Open the Subscribe.js
file under the components
folder and make these changes.
Import the
useState
hook fromreact
to manage a few states. Add this line at the top of the file.import { useState } from 'react';
Create three state variables to handle the email from the user input and the error, success message from the API call. Add these three lines at the beginning of the
Subscribe
function as,const Subscribe = () => { const [email, setEmail] = useState(''); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); return ( ..... {/* Rest of the code as is */} .... ) }
Next, handle two events. One is to capture the user input in the email field, and the second is to handle the for submit.
... ... <form className="relative my-4" onSubmit={subscribeMe}> <input onChange={changeEmail}
Now is the time to define both
subscribeMe
andchangeEmail
methods.const subscribeMe = async (event) => { event.preventDefault(); const res = await fetch("/api/subscribe", { body: JSON.stringify({ email: email }), headers: { 'Content-Type': 'application/json' }, method: "POST", }); const { error, message } = await res.json(); if (error) { setError(error); } else { setSuccess(message); } }; const changeEmail = (event) => { const email = event.target.value; setEmail(email); }
In the
subscribeMe
method, we call the API/api/subscribe
, passing the email value as the payload. Then we handle the error and success message.Last, let's show the success and error message in the UI. Add this code right after the form element.
{success ? <span className="flex items-center text-sm font-bold text-green-700"> {success} </span> : <span className="flex items-center text-sm font-bold text-red-800"> {error} </span> }
Great, now go to the app and provide an email id to register. As we have turned off the email verification, you can test it with an arbitrary email id. Please take a look into the entire source file from here.
To verify, the email address got added successfully, got to the subscribers page of your account. You should see this new email id added,
Please make sure to use the email verification by turning on
double_opt_in: true
in the API function for production usage.
Try the same email id again to attempt to register!
Yep, you will get that error. That's all. The subscription works well.
Get the Subscriber Count
Alright, let's get the subscriber count. So we will now write a serverless function to fetch the subscriber count. Please create a file called subscribers.js
under the pages/api
folder with the following content.
export default async function handler(_, res) {
const API_KEY = process.env.REVUE_API_KEY;
const response = await fetch('https://www.getrevue.co/api/v2/subscribers', {
headers: {
Authorization: `Token ${API_KEY}`,
'Content-Type': 'application/json'
},
method: 'GET'
});
const data = await response.json();
const count = data.length;
res.setHeader(
'Cache-Control',
'public, s-maxage=1200, stale-while-revalidate=600'
);
return res.status(200).json({ count });
}
We use the Revue API to fetch the subscriber list and then return the count as a response. So, now we have to use the /api/subscribers
URI to fetch the count. Let's do it.
Update the UI Code to Fetch Subscriber Count
We need to fetch the subscriber count when the Subscribe
component loads. Also, if there is a new subscriber, we need to show the updated count in the UI. Next.js
supports two kinds of pre-rendering
,
Static Generation(SSG)
: In this case, everything is precompiled, prebuilt and cached. You do not see changes in your content until there is another build. It works best when you deal with static data like blog articles.Server-Side Rendering(SSR)
: Here, the data for a page generates on demand for each request.
We prefer static generation as much as possible but may not avoid the server-side rendering in some cases. For our app, we will use SWR
. As described here,
SWR
is derived fromstale-while-revalidate
, a HTTP cache invalidation strategy popularized byHTTP RFC 5861
. SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.
With Next.js pre-rendering
support and SWR
, you can pre-render the page for SEO and allow caching, revalidation, and re-fetching at intervals on the client side.
Install
swr
library using the command,npm install swr #Or, yarn add swr
The
swr
library gives us a hook calleduseSWR
. It takes two parameters, akey
and a fetcher function. Thekey
is a string value, usually the API URL that we will pass to thefetcher
function, and thefetcher
function can be an asynchronous function. So, let us create a simple fetcher function.Please create a
utils
folder at the root of the project and create afetcher.js
file with the following content,export default async function Fetcher(...args) { const res = await fetch(...args); return res.json(); }
Next, in the
components/Subscribe.js
file, include these two imports.import useSWR from 'swr'; import fetcher from '../utils/fetcher';
Now we can use the
useSWR
hook to pass the API(api/subscribers
) and the fetcher function.const Subscribe = () => { const [email, setEmail] = useState(''); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); // --- above is old code --- const { data } = useSWR('/api/subscribers', fetcher); const subscriberCount = data?.count;
Please note, we use the
JavaScript optional chaining(?.)
feature to get the count value. It handles theundefined
value much safely.Every time the data gets changed at the back-end, the
subscriberCount
variable will have the latest count.Last is to use the
subscriberCount
state variable instead of the hardcoded value.<p className="text-sm text-gray-800 dark:text-gray-200"> { subscriberCount } subscribers . 3 issues </p>
That's all. Refresh the app and see the actual count reflecting.
Get the Issue List
Now we need to get the issue list and the count of published issues. So we have to write a serverless function again to fetch these details. But wait, I'm not going to do that in this tutorial. Please take it as an exercise to try out.
Hint: You need to use this Revue API to fetch the data => GET /v2/issues
. If you need help, the API code is here, and the component changes are here to refer to.
In the end, the UI should have the actual issue count and a list of published issues like this(I have one test issue).
Let's Deploy
Congratulations!!! The app is ready to use. But, it is available only with you. Let's deploy it publicly. We will use the Vercel platform to deploy our app. It is super easy to deploy a Next.js app on Vercel using a few simple steps. To make it happen, please commit and push all your code changes to the GitHub
repository.
Create an account with Vercel, log in and click on the
New Project
button to get started.Next, import your project from GitHub.
Now, you need to configure your project. For a Next.js project, you hardly need to make any changes to the build and other parameters. If your app is depending on any Environment Variables, you need to add them one by one. In our case, we have one. So let's add it. Then, click on the
Deploy
button.Congratulations!!! You have deployed the app successfully on Vercel. Now you can access the app publicly using the URL generated by the deployment process.
Post-deployment, you can perform many checks and additional configurations based on your needs. If your app has one or more serverless functions, you can see the live execution logs from your project's Functions
tab. The image below shows the log for our functions.
In Summary
Next.js
is the future(arguably?) for React-based projects. It is easy to set up, learn, and use. Thetailwindcss
is a developer-friendly CSS library to style the app.Revue
is an amazing newsletter service.- Your users, customers like the
oneness
and thebelongingness
. Then why not get the newsletter service within the app/site itself and manage it? Next.js APIs
are the Node.jsserverless functions
in the background. It is a great way to fetch, interact with back-end services.- It is super easy to deploy and maintain your Next.js app(including serverless functions) using
Vercel
. - Similar to this, you can integrate many other services like GitHub, your blog, and many more that I'll cover in my upcoming post.
That's all. I hope you enjoyed building it with me. Please share/like this article and the video tutorial so that it reaches others as well.
Let's connect. Please find me on Twitter(@tapasadhikary), sharing thoughts, tips, and code practices. Would you please give a follow? You can hit the Subscribe button at the top of the page to get an email notification on my latest posts.