• Blog
  • Documentation
  • Courses
  • Changelog
  • AI Starters
  • UI Kit
  • FAQ
  • Supamode
    New
  • Pricing

Launch your next SaaS in record time with Makerkit, a React SaaS Boilerplate for Next.js and Supabase.

Makerkit is a product of Makerkit Pte Ltd (registered in the Republic of Singapore)Company Registration No: 202407149CFor support or inquiries, please contact us

About
  • FAQ
  • Contact
  • Verify your Discord
  • Consultation
  • Open Source
  • Become an Affiliate
Product
  • Documentation
  • Blog
  • Changelog
  • UI Blocks
  • Figma UI Kit
  • AI SaaS Starters
License
  • Activate License
  • Upgrade License
  • Invite Member
Legal
  • Terms of License

How to make a Convertkit sign-up form with Next.js

Jul 24, 2022

Want to grow your Newsletter list? Learn how to build a sign-up form with Convertkit and Next.js

next

Convertkit is one of the most used services for creating and sending newsletters, or for selling digital products.

Sign-up forms are very important before launching your SaaS because it allows you to contact your prospective customers, get feedback, and generally create an audience for your product: this is why the Makerkit's SaaS starter helps you during the pre-launch phase of your SaaS by giving you the option of using a Convertkit or a EmailOctopus sign-up form, two among the favorite email marketing providers for developers and startups.

While Convertkit does offer various built-in sign-up forms, that means having to host one more script onto your website, which also means a heavier page.

When I built Makerkit, I decided that adding yet another script wasn't worth it: fortunately, Convertkit does allow subscriptions using an API we can use for building a lightweight form without any need for an external third-party script.

Building the Sign-Up Form with Makerkit's UI Components

Retrieve the API Key

First, we have to retrieve the API key from your Convertkit Dashboard, click the link to visit it.

Your API key needs to be added to the Next.js environment variables of your application so that we can access it as process.env.CONVERTKIT_API_KEY.

Retrieve the Form ID

Before being able to headlessly subscribe users to a form, we need to create one in Convertkit. In fact, to subscribe users with the Converkit API we need to specify a form ID.

To do so, visit the forms page in Convertkit, create a form, and grab the ID of the form: this is available from the URL as the last segment, or by clicking on "Publish" where it will be available within the script coding block with the data-ui attribute.

Grab the form ID and store it somewhere in your environment or your configuration.

Let's write the sign-up form's UI

It's time to write the UI of our sign-up form. The code below will look just like the one used in this website:

Writing the Component's UI

The Form's UI is very simple: a form that when submitted will call its handler onSubmit.

<>
<form
target="_blank"
className={`space-around flex w-full flex-grow justify-center`}
onSubmit={onSubmit}
>
<input
type="email"
className="TextInput w-full !rounded-tr-none !rounded-br-none border-r-transparent py-1 text-sm hover:border-r-transparent md:w-80 md:text-base"
name={name}
aria-label="Your email address"
placeholder="your@email.com"
required
/>
<button className="Button min-w-[6rem] rounded-tl-none rounded-bl-none text-sm md:text-base">
{children ?? 'Sign up'}
</button>
</form>
<p className={'mt-2 text-center text-sm md:text-xs'}>
Subscribe to our newsletter to receive updates
</p>
</>

Submitting an HTTP request to the API

When the user subscribes and the onSubmit is called, we have to make an HTTP request to our API endpoint at /api/convertkit/subscribe.

To do so, first we create a state property success:

  • if it's false, the request did not work
  • if it's true, the request worked

Here is the handler's code:

const [success, setSuccess] = useState<boolean | undefined>();
const onSubmit: FormEventHandler = useCallback(
async (event) => {
event.preventDefault();
const target = event.target as HTMLFormElement;
const data = new FormData(target);
const email = data.get(name);
const body = JSON.stringify({
formId,
email,
});
const headers = new Headers({
'Content-Type': 'application/json; charset=utf-8',
});
try {
await fetch(`/api/convertkit/subscribe`, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers,
body,
});
setSuccess(true);
} catch {
setSuccess(false);
}
},
[formId]
);

Depending on the result of the request, we display either a success message or an error.

The full Form Component

Below, you can find the full component's code:

ConvertkitSignupForm.tsx

import { FormEventHandler, useCallback, useState } from 'react';
const ConvertkitSignupForm: React.FC<
React.PropsWithChildren<{
formId: string;
}>
> = ({ formId, children }) => {
const name = 'email';
const [success, setSuccess] = useState<boolean | undefined>();
const onSubmit: FormEventHandler = useCallback(
async (event) => {
event.preventDefault();
const target = event.target as HTMLFormElement;
const data = new FormData(target);
const email = data.get(name);
const body = JSON.stringify({
formId,
email,
});
const headers = new Headers({
'Content-Type': 'application/json; charset=utf-8',
});
try {
await fetch(`/api/convertkit/subscribe`, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers,
body,
});
setSuccess(true);
} catch {
setSuccess(false);
}
},
[formId]
);
if (success === false) {
return <p>Apologies, an error occurred</p>;
}
if (success) {
return <p>You&apos;re in! Thank you for subscribing.</p>;
}
return (
<>
<form
target="_blank"
className={`space-around flex w-full flex-grow justify-center`}
onSubmit={onSubmit}
>
<input
type="email"
className="TextInput w-full !rounded-tr-none !rounded-br-none border-r-transparent py-1 text-sm hover:border-r-transparent md:w-80 md:text-base"
name={name}
aria-label="Your email address"
placeholder="your@email.com"
required
/>
<button className="Button min-w-[6rem] rounded-tl-none rounded-bl-none text-sm md:text-base">
{children ?? 'Sign up'}
</button>
</form>
<p className={'mt-2 text-center text-sm md:text-xs'}>
Subscribe to our newsletter to receive updates
</p>
</>
);
};
export default ConvertkitSignupForm;

Building the API to subscribe the user to a sign-up form

Below is the API handler's code that takes care of calling the Convertkit API and subscribe the user:

import { NextApiRequest, NextApiResponse } from 'next';
const API_KEY = process.env.CONVERTKIT_API_KEY;
const BASE_URL = 'https://api.convertkit.com/v3';
function subscribeToForm(
params: { formId: string; email: string }
) {
const url = [BASE_URL, `forms`, params.formId, 'subscribe'].join('/');
const headers = new Headers({
'Content-Type': 'application/json; charset=utf-8',
});
return fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({
api_key: API_KEY,
email: params.email,
}),
});
}
async function subscribeToFormHandler(
req: NextApiRequest,
res: NextApiResponse
) {
const body = req.body;
// best to validate this with Zod...
await subscribeToForm({
formId: body.formId,
email: body.email,
});
return res.send({ success: true });
}
export default subscribeToFormHandler;

And that's it! If you like me don't like the idea of adding yet another huge JS bundle to your client, you can use the script above and create a Convertkit Sign-up form quickly and easily.

Of course, the code above is already included in the Makerkit's SaaS starter for Next.js. Take a look!

Some other posts you might like...
Sep 25, 2025Mastering AI-Driven Development: Claude Code & Makerkit Best PracticesA comprehensive guide to building production-ready SaaS features using Claude Code's intelligent agents and Makerkit's PRD functionality
Jun 9, 2025Build a SaaS with Claude CodeThis is a step-by-step guide to building an AI Content Repurposer SaaS by vibe-coding with Claude Code and Makerkit.
Apr 23, 2025Next.js Security: A Comprehensive Guide how to secure your Next.js applicationA comprehensive guide to securing your Next.js application, focusing on practical strategies to protect your application and user data from common vulnerabilities.
Jan 17, 2025Best Practices for Building a SaaS with Windsurf and MakerkitWindsurf is a new AI-powered editor taking the developer experience to the next level. With the new optimized rules for Makerkit, building a SaaS just got a lot easier!
Jan 16, 2025Best Practices for Building a SaaS with Cursor and MakerkitCursor is the hottest AI editor in the market. With the new optimized rules for Makerkit, building a SaaS just got a lot easier!
Dec 26, 2024Choosing the best hosting provider for your Next.js applicationIn this post, we'll show you how to choose the best hosting provider for your Next.js application.