[HOW-TO] Integrate Hashnode Newsletter Subscription into Your Next.JS Website

Using Next.JS 13+, TailwindCSS, and react-hook-form

Having a blog AND a newsletter is just too much for me. I appreciate that Hashnode lets you combine both into one, publishing your blog posts to subscriber emails. I also like to have ultimate control over the look and feel of my blog to have it seamlessly integrated into my website. This makes newsletter sign-ups tricky, though.

So, in this tutorial, I'll guide you through integrating Hashnode's newsletter subscription directly into your Next.JS website. This feature enhances user engagement by allowing visitors to subscribe to your blog as a newsletter without leaving your site. Let's dive in!

Step 1: Setting Up Your Project

Assuming you're familiar with the basics of package installation and setup, let's quickly go over the initial steps:

  1. Create a Next.JS Project: Initialize a new Next.JS project using create-next-app.

     npx create-next-app@latest my-nextjs-blog
    

    Note: You'll need to be using the App Router for this tutorial to work for you.

  2. Add TailwindCSS: Install TailwindCSS. This will be our styling framework.

     npm install -D tailwindcss
     npx tailwindcss init
    
  3. Setup React Hook Form: Install react-hook-form for managing form states and validations.

     npm install react-hook-form
    

With these initial setups out of the way, let's focus on the core functionality.

Step 2: Building the Newsletter Subscription Component

Now that your project is set up, let's build the NewsletterForm component. This component will handle user input, validate the email address, and submit the data to your API endpoint.

Creating the NewsletterForm Component

Create a new file named NewsletterForm.js in your components directory and add the following code:

// NewsletterForm.js
import { useState } from 'react';
import { useForm } from 'react-hook-form';

const NewsletterForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();
  const [loading, setLoading] = useState(false);
  const [submitMessage, setSubmitMessage] = useState(
    'You will receive a confirmation email and you may unsubscribe at any time.'
  );

  const onSubmit = async (data) => {
    setLoading(true);
    const response = await fetch('/api/newsletter', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    });

    if (!response.ok) {
      setLoading(false);
      setSubmitMessage('Something went wrong. Please try again.');
      return;
    }

    setLoading(false);
    setSubmitMessage('Thank you for subscribing!');
  };

  return (
    <div className="max-w-2xl mx-auto py-12 flex flex-col justify-center">
      <h3 className="text-4xl font-serif font-bold my-4 text-center">
        Subscribe & Never Miss a Post
      </h3>
      <form onSubmit={handleSubmit(onSubmit)} className="w-full mx-auto">
        <input
          type="email"
          placeholder="Your email"
          className={`input input-bordered w-full ${
            errors.email ? 'input-error' : ''
          }`}
          {...register('email', {
            required: 'Email is required',
            pattern: {
              value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
              message: 'Invalid email address',
            },
          })}
        />
        <p className="text-error">
          {errors.email?.message}
        </p>
        <button
          type="submit"
          className="btn btn-primary my-4"
          disabled={loading}
        >
          {loading ? 'Subscribing...' : 'Subscribe'}
        </button>
        <p className="text-center">{submitMessage}</p>
      </form>
    </div>
  );
};

export default NewsletterForm;

Step 2: Building the Newsletter Subscription Component

Now that your project is set up, let's build the NewsletterForm component. This component will handle user input, validate the email address, and submit the data to your API endpoint.

1. Creating the NewsletterForm Component

Create a new file named NewsletterForm.js in your components directory and add the following code:

// NewsletterForm.js
import { useState } from 'react';
import { useForm } from 'react-hook-form';

const NewsletterForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();
  const [loading, setLoading] = useState(false);
  const [submitMessage, setSubmitMessage] = useState(
    'You will receive a confirmation email and you may unsubscribe at any time.'
  );

  const onSubmit = async (data) => {
    setLoading(true);
    const response = await fetch('/api/newsletter', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    });

    if (!response.ok) {
      setLoading(false);
      setSubmitMessage('Something went wrong. Please try again.');
      return;
    }

    setLoading(false);
    setSubmitMessage('Thank you for subscribing!');
  };

  return (
    <div className="max-w-2xl mx-auto py-12 flex flex-col justify-center">
      <h3 className="text-4xl font-serif font-bold my-4 text-center">
        Subscribe & Never Miss a Post
      </h3>
      <form onSubmit={handleSubmit(onSubmit)} className="w-full mx-auto">
        <input
          type="email"
          placeholder="Your email"
          className={`input input-bordered w-full ${
            errors.email ? 'input-error' : ''
          }`}
          {...register('email', {
            required: 'Email is required',
            pattern: {
              value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
              message: 'Invalid email address',
            },
          })}
        />
        <p className="text-error">
          {errors.email?.message}
        </p>
        <button
          type="submit"
          className="btn btn-primary my-4"
          disabled={loading}
        >
          {loading ? 'Subscribing...' : 'Subscribe'}
        </button>
        <p className="text-center">{submitMessage}</p>
      </form>
    </div>
  );
};

export default NewsletterForm;

In this component:

  • We use react-hook-form for form handling, which simplifies managing form states and validations.

  • The onSubmit function handles the form submission. It sends the email data to your API endpoint and updates the UI based on the response.

  • TailwindCSS classes are used for styling.

Integrating the Component

To use the NewsletterForm component, import it into the page where you want it to appear. For instance, in your index.js or a specific blog post page, add:

import NewsletterForm from '../components/NewsletterForm';

const Home = () => {
  return (
    <div>
      {/* Other page content */}
      <NewsletterForm />
    </div>
  );
};

export default Home;

With these steps, your NewsletterForm component is ready and integrated into your Next.js application. The component handles user inputs, validates the email, and interacts with your server-side API to manage newsletter subscriptions. You can use the NewsletterForm component anywhere in your application, add it any place you like!


Step 3: Implementing the API Endpoint

After setting up the client-side component, the next step is to implement the server-side logic that will handle the newsletter subscription request. This involves creating an API route in your Next.js application that interacts with the Hashnode API.

1. Creating the API Route

Create a new file under the pages/api directory in your Next.js project, named newsletter.js. This file will contain your API endpoint logic.

Here's how your newsletter.js should look:

// pages/api/newsletter.js
import { NextResponse } from "next/server";

export async function POST(req) {
  try {
    const body = await req.json();
    const { email } = body;

    const requestBody = {
      query: `
        mutation SubscribeToNewsletter($input: SubscribeToNewsletterInput!) {
          subscribeToNewsletter(input: $input) {
            status
          }
        }
      `,
      variables: {
        input: {
          publicationId: "YOUR_PUBLICATION_ID", // Replace with your Hashnode publication ID
          email: email,
        },
      },
    };

    const response = await fetch("https://gql.hashnode.com/", {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        // Include any additional headers required by Hashnode API
      },
      body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
      const errorData = await response.json();
      console.error('Error response:', errorData.errors);
      return NextResponse.json({ message: 'Subscription failed', error: errorData.errors }, { status: 500 });
    }

    const data = await response.json();
    return NextResponse.json({ message: 'Subscription successful', data }, { status: 200 });
  } catch (error) {
    console.error("Server error:", error);
    return NextResponse.json({ message: 'Subscription failed', error }, { status: 500 });
  }
}

export default POST;

Don't forget to replace "YOUR_PUBLICATION_ID" with your Hashnode blog's publication ID.

In this code:

  • The POST function is an async function that handles POST requests to /api/newsletter.

  • It sends a GraphQL mutation to the Hashnode API for subscribing a user to the newsletter.

  • The function includes error handling to respond appropriately in case of failures.

2. Error Handling and Response

  • The server-side code catches any errors during the API call and returns a JSON response with an error message.

  • On a successful API call, it returns a success message.


Step 4: Testing and Deployment

1. Local Testing

  • Thoroughly test the newsletter subscription feature in your local environment.

  • Check for both successful and failed subscription attempts to ensure proper handling.

2. Deployment

  • Once you're satisfied with the functionality in the local environment, deploy your Next.js application.

  • Use a service like Vercel, Netlify, or any other hosting platform that supports Next.js.

  • Test the newsletter subscription feature in the production environment to ensure everything works as expected post-deployment.


Conclusion

By following these steps, you've successfully integrated Hashnode newsletter subscription functionality into your Next.js website. This integration not only enhances user engagement but also provides a seamless experience for your visitors to stay connected with your content.

Note: Hashnode will always send a confirmation email to new subscribers to ensure that they intend to subscribe to your newsletter. They will not receive blog posts as a newsletter unless they confirm this email.

Feel free to customize and extend this implementation to suit your site's specific needs and aesthetics. Enjoy building and engaging with your audience!