Build Blog Newsletter with Sendinblue and Next.js

M. K. Bughowi

January 2023, 23

11 min read

Build Blog Newsletter with Sendinblue and Next.js

Building an email newsletter for your Next.js blog can be a great way to keep your viewers informed about your business and articles. By integrating Next.js with an email marketing platform like Sendinblue, you can easily create and send newsletters to a large number of recipients.

Introduction to Sendinblue

Sendinblue is an all-in-one marketing platform that offers a variety of tools for businesses to connect with their customers and grow their business. These tools include email marketing, SMS marketing, chat and CRM, and automation. With Sendinblue, you can create and send email campaigns, automate your email marketing, segment your contacts, and track the performance of your campaigns. The platform also offers a drag-and-drop editor to design professional-looking emails, and a variety of templates to choose from. Additionally, it provides a robust set of APIs that allow you to integrate with other platforms like Next.js and other websites, or even with your own software.

Create Sendinblue Account

The first step in building an email newsletter with Next.js is to set up a Sendinblue account if you don’t already have one. Once you have an account, you can create a new campaign and design your newsletter template.

Creating a Sendinblue account is a simple process. Here are the steps to follow:

  1. Go to the Sendinblue website
  2. Click on the Sign Up Free button on the top right corner of the page
  3. Fill in your personal information, such as your name, email address, and password. Or you can simply Sign Up with Google or Apple account.
  4. Verify your email address by clicking on the link sent to your email
  5. Once you have verified your email, you will be prompted to log in to your account.
  6. Once you have logged in, you will be taken to your account dashboard, where you can start creating campaigns, designing your templates, and managing your contacts.

Creating the form

Now, let’s create a simple form that takes your viewers email.

First, create a new component called NewsletterForm.tsx. Start creating your own newsletter form component, or you can simply copy this code.

./components/NewsletterForm.tsx
const NewsletterForm = () => {
return (
<div className="mx-auto w-full max-w-xl rounded border border-gray-700 bg-slate-900 p-5">
<p className="text-lg font-bold">Subscribe to the newsletter</p>
<p className="py-1">
Get emails from me about web development, programming, tech, and early access to new
articles.
</p>
<form className="mx-auto mt-1 w-full" method="post" target="_blank">
<div className="flex w-full flex-wrap items-center justify-center gap-2">
<input
type="email"
id="email"
className="block flex-auto rounded border border-darkSecondary bg-darkPrimary p-2 text-sm text-slate-400 focus:border-blue-500 focus:ring-blue-500"
placeholder="name@gmail.com"
required
/>
<button
type="submit"
className="w-28 flex-1 rounded bg-blueAccent p-1.5 text-center font-bold text-slate-200 transition-colors hover:bg-blue-500"
>
Subscribe
</button>
</div>
</form>
</div>
);
};
export default NewsletterForm;

Now, our Newsletter Form will looks like this

Subscribe Newsletter Component

Creating the custom hook

We need to create a custom hook for handling the form. By abstracting it with a custom hook is a great way to keep your code organized and reusable. Here’s an example of how you might create a custom hook for handling the newsletter form.

./utils/hooks/useNewsletter.ts
import { useRef, useState } from 'react';
export enum Form {
Initial,
Loading,
Success,
Error,
}
export type FormState = {
state: Form;
message?: string;
};
export function useNewsletter() {
const [form, setForm] = useState<FormState>({ state: Form.Initial });
}

The hook also declares a variable called inputEl which is initialized with the useRef hook. This variable is used to keep track of the email input element in the form.

./utils/hooks/useNewsletter.ts
import { useRef, useState } from 'react';
export enum Form {
Initial,
Loading,
Success,
Error,
}
export type FormState = {
state: Form;
message?: string;
};
export function useNewsletter() {
const [form, setForm] = useState<FormState>({ state: Form.Initial });
const inputEl = useRef<HTMLInputElement>(null);
}

The hook starts by importing the useState and useRef hooks from the React library. useState is used to manage the state of the form, and useRef is used to keep track of the input element in the form.

Then, it declares a state variable called form that is initialized with an object that has two properties, state: Form.Initial and message: undefined. The state property is used to keep track of the status of the form, it can be Form.Initial, Form.Loading, Form.Success or Form.Error. The message property is used to display the message depending of the status of the form.

Next, the hook exports a function called subscribe that handles the form submission. This function takes one parameter e of type React.FormEvent, which is the event object that is passed to the function when the form is submitted.

Inside the function, the first thing it does is calling the preventDefault method on the event object to prevent the page from refreshing when the form is submitted. Then it changes the state of the form to Form.Loading

./utils/hooks/useNewsletter.ts
import { useRef, useState } from 'react';
export enum Form {
Initial,
Loading,
Success,
Error,
}
export type FormState = {
state: Form;
message?: string;
};
export function useNewsletter() {
const [form, setForm] = useState<FormState>({ state: Form.Initial });
const inputEl = useRef<HTMLInputElement>(null);
async function subscribe(e: React.FormEvent) {
e.preventDefault();
setForm({ state: Form.Loading });
}
}

After that, it uses the fetch API to make a POST request to the endpoint /api/newsletter with the email entered in the input field as the body of the request. The headers are set to Content-Type: application/json.

Once the fetch request is complete, it parses the response as json and destructures the object to get the value of the error property. If the error property is not undefined, it means that the request failed, it updates the state of the form to Form.Error and sets the message to the error returned.

./utils/hooks/useNewsletter.ts
import { useRef, useState } from 'react';
export enum Form {
Initial,
Loading,
Success,
Error,
}
export type FormState = {
state: Form;
message?: string;
};
export function useNewsletter() {
const [form, setForm] = useState<FormState>({ state: Form.Initial });
const inputEl = useRef<HTMLInputElement>(null);
async function subscribe(e: React.FormEvent) {
e.preventDefault();
setForm({ state: Form.Loading });
const res = await fetch(`/api/newsletter`, {
body: JSON.stringify({
email: inputEl.current!.value,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
});
const { error } = await res.json();
if (error) {
setForm({
state: Form.Error,
message: error,
});
return;
}
}
}

If there was no error, it sets the value of the input field to empty string, and updates the state of the form to Form.Success and sets the message to “Success! You’ve been added to the list!”

./utils/hooks/useNewsletter.ts
import { useRef, useState } from 'react';
export enum Form {
Initial,
Loading,
Success,
Error,
}
export type FormState = {
state: Form;
message?: string;
};
export function useNewsletter() {
const [form, setForm] = useState<FormState>({ state: Form.Initial });
const inputEl = useRef<HTMLInputElement>(null);
async function subscribe(e: React.FormEvent) {
e.preventDefault();
setForm({ state: Form.Loading });
const res = await fetch(`/api/newsletter`, {
body: JSON.stringify({
email: inputEl.current!.value,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
});
const { error } = await res.json();
if (error) {
setForm({
state: Form.Error,
message: error,
});
return;
}
inputEl.current!.value = '';
setForm({
state: Form.Success,
message: `Success! You've been added to the list!`,
});
}
}

Finally, the hook returns an object containing the subscribe function, the inputEl variable and the form state variable. These are the values that are needed by the component that uses this hook to render the form and handle the form submission.

./utils/hooks/useNewsletter.ts
import { useRef, useState } from 'react';
export enum Form {
Initial,
Loading,
Success,
Error,
}
export type FormState = {
state: Form;
message?: string;
};
export function useNewsletter() {
const [form, setForm] = useState<FormState>({ state: Form.Initial });
const inputEl = useRef<HTMLInputElement>(null);
async function subscribe(e: React.FormEvent) {
e.preventDefault();
setForm({ state: Form.Loading });
const res = await fetch(`/api/newsletter`, {
body: JSON.stringify({
email: inputEl.current!.value,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
});
const { error } = await res.json();
if (error) {
setForm({
state: Form.Error,
message: error,
});
return;
}
inputEl.current!.value = '';
setForm({
state: Form.Success,
message: `Success! You've been added to the list!`,
});
}
return { subscribe, inputEl, form };
}

Save Sendinblue API key

To make Sendinblue API request, we have to include the Sendinblue API key in the request headers. In order to get your Sendinblue API key, you need to sign in and follow this steps:

  1. Click on the “Settings” icon in the top right corner of the page.
  2. In the settings menu, click on the “SMTP & API” tab.
  3. In the SMTP & API page, you will find your API key under the “API v3” section.
  4. Click on the “Create a v3 API key” button to generate a new key if you don’t have any yet.
  5. You should copy the key and keep it in a safe place, you will use it in your application when you make requests to the Sendinblue API.

Be sure to keep your API key secure and never share it with anyone or include it in your codebase. It’s also important to note that you should use environment variables to store the api key and other sensitive information, instead of hardcoding them in your code.

Create .env file in root folder of your project. In the .env file, add a new variable called SENDINBLUE_API_KEY and set it to your Sendinblue API key. For example:

.env
SENDINBLUE_API_KEY=your_api_key_here

Creating the API route

To handle the form submission from the useNewsletter hook, you will need to create an API route in your Next.js application that will handle the POST request made by the hook. Here’s an example of how you might create such an API route:

  1. Open or create api folder inside pages directory
  2. Inside the api folder, create a new file called newsletter.ts
  3. In newsletter.ts, you will use the next/api route to define the route that will handle the newsletter subscription. Here’s an example of what the code might look like:
./pages/api/newsletter.ts
import { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email is required' });
}
const result = await fetch('https://api.sendinblue.com/v3/contacts', {
method: 'POST',
headers: {
accept: 'application/json',
'Content-Type': 'application/json',
'api-key': process.env.SENDINBLUE_API_KEY!,
},
body: JSON.stringify({ updateEnabled: false, email: email, listIds: [5] }),
});
const data = await result.json();
if (!result.ok) {
return res.status(500).json({ error: data.error.email[0] });
}
return res.status(201).json({ error: '' });
}
  1. In the above code, the handler function is an async function that handles the incoming POST request and is exported as the default export of the file. It first checks if the request body contain email, if not, it sends a 400 Bad Request with response “Email is required”.
  2. Next, it uses the fetch API to make a POST request to the Sendinblue API endpoint for creating a new contact, passing in the necessary headers, such as the api-key, content type, and accept. It also send the body request that contains email address, updateEnabled flag and the listIds that the contact should be added to.
  3. After the request is complete, it checks the response status. If the status is not ok, it sends a 500 Internal Server Error status code and the error message returned from the API. If the request was successful, it sends a 201 Created status code with an empty error message.

Sending the data to Sendinblue

Let’s get back to NewsletterForm component and add the useNewsletter hooks to handle the form submission.

./components/NewsletterForm.tsx
import { useNewsletter, Form } from '@utils/hooks';
const NewsletterForm = () => {
const { form, subscribe, inputEl } = useNewsletter();
return (
<div className="mx-auto w-full max-w-xl rounded border border-gray-700 bg-slate-900 p-5">
<p className="text-lg font-bold">Subscribe to the newsletter</p>
<p className="py-1">
Get emails from me about web development, programming, tech, and early access to new
articles.
</p>
<form onSubmit={subscribe} className="mx-auto mt-1 w-full" method="post" target="_blank">
<div className="flex w-full flex-wrap items-center justify-center gap-2">
<input
type="email"
ref={inputEl}
id="email"
className="block flex-auto rounded border border-slate-700 bg-slate-700 p-2 text-sm text-slate-400 focus:border-blue-500 focus:ring-blue-500"
placeholder="name@gmail.com"
required
/>
<button
type="submit"
className="w-28 flex-1 rounded bg-blue-600 p-1.5 text-center font-bold text-slate-200 transition-colors hover:bg-blue-500"
>
{form.state === Form.Loading ? <span>Loading...</span> : 'Subscribe'}
</button>
</div>
{form.state === Form.Success ? (
<div className="mt-4">
<span className="font-bold text-green-600">
<span>&#128522;</span> Thank you for subscribing my newsletters
</span>
</div>
) : (
<span></span>
)}
</form>
</div>
);
};
export default NewsletterForm;

Conclusion

Creating an email newsletter with Sendinblue and Next.js is a great way to keep in touch with your users and keep them informed about your business or project. The process of setting up a newsletter with these technologies involves several steps, such as creating a Sendinblue account, setting up a Next.js project, creating a form for email subscriptions, handling form submissions with a custom hook, and creating an API route to handle the form submissions and interact with the Sendinblue API.

One of the main benefits of using Next.js for this task is its ability to handle server-side rendering and its built-in support for API routes, which makes it easy to handle form submissions and interact with external APIs.

It’s important to keep in mind that when working with API keys and personal data, you should use environment variables to store the api key and other sensitive information, instead of hardcoding them in your code. Additionally, you should handle the errors that may occur during the interaction with the api, and also have a proper unsubscription process in place.

Overall, creating an email newsletter with Sendinblue and Next.js is a powerful way to keep in touch with your users.

Edit this page Tweet this article