Integrating Giscus Comments into a Next.js Blog

August 17, 2024

Integrating Giscus Comments into a Next.js Blog

In today’s digital landscape, user interaction and engagement are crucial for any blog or website. One effective way to foster this interaction is through a robust comment system. If you're using Next.js for your blog, you might be interested in integrating a comment system that is not only user-friendly but also leverages the power of GitHub. Enter Giscus—a modern comment system built on GitHub Discussions. This guide will walk you through integrating Giscus into your Next.js blog to enhance user engagement with minimal fuss.

What is Giscus?

Giscus is a lightweight and easy-to-integrate comment system that uses GitHub Discussions as its backend. This means comments are stored directly in your GitHub repository, offering a seamless experience for users who are already familiar with GitHub. It supports features like reactions, threading, and more, while also benefiting from GitHub’s robust infrastructure.

Why Use Giscus?

  1. Integration with GitHub: Leverage the power of GitHub Discussions for managing comments.
  2. Enhanced Security: Benefit from GitHub’s security protocols.
  3. Ease of Use: Simple setup with minimal configuration.
  4. Customization: Various themes and options to fit your blog's style.

Setting Up Giscus for Your Next.js Blog

Step 1: Prepare Your GitHub Repository

Before integrating Giscus into your Next.js blog, ensure you have a GitHub repository with Discussions enabled:

  1. Create a GitHub Repository: If you don’t have one, create a new repository.
  2. Enable Discussions: Navigate to your repository’s settings, find the "Features" section, and enable "Discussions."

Step 2: Configure Giscus

  1. Visit the Giscus Website: Go to Giscus and click on "Get Started."
  2. Fill in the Configuration Details: Provide the required details like your GitHub repository name, discussion category, and theme preferences.
  3. Generate the Script: Giscus will provide you with a script tag containing your configuration.

Step 3: Create a Client Component in Next.js

In Next.js, client-side components can use hooks like useEffect. To include Giscus, create a client-side component:

components/GiscusComment.js

'use client'; // Ensure this component is rendered on the client side

import { useEffect } from 'react';

const GiscusComment = ({ repo, repoId, category, categoryId, mapping, theme }) => {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = 'https://giscus.app/client.js';
    script.setAttribute('data-repo', repo);
    script.setAttribute('data-repo-id', repoId);
    script.setAttribute('data-category', category);
    script.setAttribute('data-category-id', categoryId);
    script.setAttribute('data-mapping', mapping);
    script.setAttribute('data-strict', '1');
    script.setAttribute('data-reactions-enabled', '1');
    script.setAttribute('data-emit-metadata', '0');
    script.setAttribute('data-input-position', 'top');
    script.setAttribute('data-theme', theme);
    script.setAttribute('data-lang', 'en');
    script.setAttribute('data-loading', 'lazy');
    script.crossOrigin = 'anonymous';
    script.async = true;

    const container = document.getElementById('giscus-container');
    if (container) {
      container.appendChild(script);
    }

    return () => {
      if (container) {
        container.removeChild(script);
      }
    };
  }, [repo, repoId, category, categoryId, mapping, theme]);

  return <div id="giscus-container" />;
};

export default GiscusComment;

Step 4: Integrate Giscus into Your Blog Page

Modify your blog post page to include the GiscusComment component:

pages/posts/[slug].js

import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import Link from 'next/link';
import { remark } from 'remark';
import html from 'remark-html';
import GiscusComment from '@/components/GiscusComment';

// Generate static paths for each post
export async function generateStaticParams() {
  const files = fs.readdirSync(path.join(process.cwd(), 'content'));
  return files.map((filename) => ({
    slug: filename.replace('.md', ''),
  }));
}

// Fetch the content and metadata of the post
async function getPost(slug) {
  const markdownWithMeta = fs.readFileSync(path.join('content', `${slug}.md`), 'utf-8');
  const { data: frontmatter, content } = matter(markdownWithMeta);

  const processedContent = await remark().use(html).process(content);
  const contentHtml = processedContent.toString();

  return { frontmatter, contentHtml };
}

// Component to display the post content
export default async function PostPage({ params }) {
  const { slug } = params;
  const { frontmatter, contentHtml } = await getPost(slug);

  return (
    <div className="p-4 md:p-8">
      <div className="max-w-xs sm:max-w-sm md:max-w-4xl mx-auto">
        <article className="prose max-w-none">
          <header className="mb-6">
            <div className="text-center">
              <h1 className="text-xl md:text-5xl font-bold mb-2 text-wrap">{frontmatter.title}</h1>
              <p className="text-gray-500 text-sm md:text-base mb-4">
                {new Date(frontmatter.date).toLocaleString('en-US', {
                  month: 'long',
                  day: 'numeric',
                  year: 'numeric',
                })}
              </p>
              <div className="text-gray-500 mb-3 flex flex-wrap">
                {frontmatter.tags?.map((tag, index) => (
                  <Link key={index} href={`/blog/tags/${tag}`} className="text-pink-500 hover:underline mr-2">
                    #{tag}
                    {index < frontmatter.tags.length - 1 ? ',' : ''}
                  </Link>
                ))}
              </div>
            </div>
          </header>

          <div className="border border-gray-200 p-4 sm:p-6 hover:shadow-md transition-shadow rounded-md">
            <div dangerouslySetInnerHTML={{ __html: contentHtml }} />
          </div>
        </article>

        <div className="mt-8 mx-20 pb-20 md:pb-0">
          <Link href="/blog" className="text-teal-800 hover:underline">
            ← Back to Blog
          </Link>
        </div>

        <div className="mt-8">
          <GiscusComment
            repo="username/reponame"
            repoId="repoId"
            category="General"
            categoryId="YourCategoryId"
            mapping="pathname"
            theme="preferred_color_scheme" // or "light" or "dark"
          />
        </div>
      </div>
    </div>
  );
}

Conclusion

Integrating Giscus into your Next.js blog can enhance user engagement by allowing readers to leave comments directly through GitHub Discussions. This setup not only simplifies the management of comments but also ensures a seamless experience for users familiar with GitHub. By following the steps outlined in this guide, you can easily add a powerful commenting feature to your blog and leverage the benefits of GitHub’s infrastructure.

Happy blogging!

Follow Me