Here is a video of what we will be building today.
typescriptimport isValidEmail from 'utils/isValidEmail';const subscribeConvertkit = async (req, res) => {const { email } = JSON.parse(req.body);const isValid = isValidEmail(email);if (!email) {return res.status(400).json({ error: 'Email is required.' });}if (!isValid) {return res.status(400).json({ error: 'Email is invalid.' });}try {const FORM_ID = process.env.CONVERTKIT_FORM_ID;const API_KEY = process.env.CONVERTKIT_API_KEY;const API_URL = process.env.CONVERTKIT_API_URL;const response = await fetch(`${API_URL}forms/${FORM_ID}/subscribe`, {body: JSON.stringify({ email, api_key: API_KEY }),headers: { 'Content-Type': 'application/json' },method: 'POST'});// something went wrong on the convertkit serverif (response.status >= 400) {return res.status(400).json({ error: 'There was an error subscribing to the list.' });}// Successreturn res.status(201).json({ error: null });} catch (error) {return res.status(500).json({ error: error.message || error.toString() });}};export default subscribeConvertkit;
For submitting the subscription, let's create a simple handler that returns the result from the API call ๐
javascriptexport default async function addSubscriber(email) {// The location of your API routeconst url = '/api/subscribe-convertkit';const response = await fetch(url, {method: 'POST',body: JSON.stringify({ email })});const result = await response.json();return result;}
Now we have the API ready and the fetcher ready, so we can get into creating the Subscribe component
javascriptimport Container from 'components/Container';import { useState } from 'react';import { useForm } from 'react-hook-form';import addSubscriber from 'utils/addSubscriber';import Confetti from './Confetti';export default function Subscribe() {const [formState, setFormState] = useState({ state: 'initial', message: '' });const { register, handleSubmit } = useForm();const onSubmit = async data => {setFormState({ state: 'loading', message: null });const { error } = await addSubscriber(data.email);if (error) {return setFormState({ state: 'error', message: error });}return setFormState({state: 'success',message: 'Check your email to confirm your subscription'});};return (<div className="py-24 bg-gray-100"><Container><div className="sm:text-center"><div className="text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl">Subscribe to the newsletter</div><p className="max-w-2xl mx-auto mt-4 text-lg text-gray-500">Get emails from me about web development, tech, and early access to newarticles.</p></div><formonSubmit={handleSubmit(onSubmit)}className="mt-8 sm:mx-auto sm:max-w-lg sm:flex"><div className="flex-1 min-w-0"><label htmlFor="cta-email" className="sr-only">Email address</label><input{...register('email')}id="cta-email"type="email"className="block w-full px-5 py-3 text-base text-gray-900 placeholder-gray-500 border border-transparent rounded-md shadow-sm"placeholder="Enter your email"/>{formState.state === 'success' && (<div className="my-2 mb-4 ml-2 text-sm font-semibold text-green-700 dark:text-green-500">{formState.message}</div>)}{formState.state === 'error' && (<div className="my-2 mb-4 ml-2 text-sm font-semibold text-red-700 dark:text-red-500">{formState.message}</div>)}<buttontype="submit"className="block w-full px-5 py-3 mt-4 text-base font-medium text-white bg-gray-600 border border-transparent rounded-md shadow hover:bg-gray-400 focus:outline-none sm:px-10">Subscribe</button></div></form></Container></div>);}
Create Dynamic Open Graph images with Next.js
In this post, I want to show you how to generate dynamic Open graph images with Next.js
How to make Confetti with React in 5 minutes ๐
How to make confetti effect with react-canvas-confetti package in React.