logo
Published on

DevBlog 3. Adding Comments with giscus

Authors
  • avatar
    Name
    seren-wib
    Twitter
Contents

How the comment box at the bottom of each post was added. This blog started from the tailwind-nextjs-starter-blog template, and the comment code was there from day one. Only the settings were missing.

1. Why giscus

OptionStored inDownside
DisqusDisqus serversComes with ads and tracking scripts. Data isn't yours
utterancesGitHub IssuesComments mix into the issue list. No threaded replies
giscusGitHub DiscussionsVisitors need a GitHub account to comment
  • A static blog with no server or database has nowhere to store comments. GitHub Discussions is borrowed as the store
  • Most readers are developers, so requiring a GitHub login isn't a problem
  • One post = one discussion. The giscus app creates it when the first comment is posted

2. What the template already had

  • data/siteMetadata.js has provider: 'giscus' and the config fields. Every value is read from environment variables
data/siteMetadata.js
comments: {
  provider: 'giscus',
  giscusConfig: {
    repo: process.env.NEXT_PUBLIC_GISCUS_REPO,
    repositoryId: process.env.NEXT_PUBLIC_GISCUS_REPOSITORY_ID,
    category: process.env.NEXT_PUBLIC_GISCUS_CATEGORY,
    categoryId: process.env.NEXT_PUBLIC_GISCUS_CATEGORY_ID,
    mapping: 'pathname',
    // ...
  },
},
  • The CSP (next.config.js) already allows giscus.app in script-src and frame-src
  • But all four environment variables were empty, and Discussions was off on the repo. So pressing "Load Comments" showed nothing

3. GitHub setup

Turn on Discussions

  • Repo Settings → General → Features → check Discussions
  • The default categories (Announcements, General, Q&A, and so on) are created right away
  • The "Start discussion" welcome post that follows is optional. It has nothing to do with comments

Install the giscus app

  • Install from github.com/apps/giscus
  • Under Repository access, pick Only select repositories and choose just the blog repo. All repositories would open private repos to the app too

Get the IDs

  • Enter the repo name on giscus.app and it shows the IDs. With gh, one GraphQL call does it too
gh api graphql -f query='{
  repository(owner: "seren-wib", name: "vercel-blog") {
    id
    discussionCategories(first: 10) { nodes { name id } }
  }
}'
  • id is the repo ID (R_...), and each category's DIC_... is its category ID

Use the General category

  • It started with Announcements, which giscus recommends. Only maintainers and apps can start new discussions there, so visitors can't open stray ones
  • But the welcome announcement and the per-post comment discussions ended up mixed together. To keep announcements on their own, comments moved to General
  • In General, visitors can also start new discussions from the Discussions tab. An unrelated thread may show up among the comment discussions now and then, which is fine at this scale
  • After switching categories, existing discussions have to be moved too. giscus only looks for discussions in the configured category, so an unmoved one hides that post's comments and a new empty discussion gets created. Move them with Change category on the discussion page or the GraphQL updateDiscussion mutation

4. Vercel environment variables

NameValue
NEXT_PUBLIC_GISCUS_REPOseren-wib/vercel-blog
NEXT_PUBLIC_GISCUS_REPOSITORY_IDR_...
NEXT_PUBLIC_GISCUS_CATEGORYGeneral
NEXT_PUBLIC_GISCUS_CATEGORY_IDDIC_...
  • None of these are secrets. They end up in the browser anyway, so they're stored as plain
  • NEXT_PUBLIC_ values are baked into the code at build time. Saving the variables alone leaves the site unchanged. It takes one Redeploy to apply them

5. Showing comments without a button

  • The template loaded giscus only after clicking "Load Comments", to save page load
  • But pliny's giscus component already creates the iframe with loading: "lazy". It only loads once you scroll near the comments, so dropping the button doesn't change the initial load
  • So the button and its state were removed and the component renders directly
components/Comments.tsx
export default function Comments({ slug }: { slug: string }) {
  if (!siteMetadata.comments?.provider) {
    return null
  }
  return <CommentsComponent commentsConfig={siteMetadata.comments} slug={slug} />
}
  • The "Discuss on Twitter" and "View on GitHub" links under each post went too. With a comment box in place, there's no use for them
  • On long posts the comments are a long scroll away, but the floating speech-bubble button at the bottom right jumps straight to #comment

6. Mapping: pathname

  • mapping: 'pathname' finds the discussion by page path. Discussion titles become paths like kr/blog/devblog/devblog-03-giscus-comments/
  • This blog serves each post at two addresses, /kr/ and /en/. The paths differ, so Korean and English posts get separate comment threads
  • Merging them would take the specific mapping with a term that drops the language prefix. For now they stay separate, since each language has its own readers
  • Renaming a slug changes the path and cuts the link to existing comments. If a post's URL ever changes, the discussion title in Discussions has to be updated too

7. Summary

  • Comments on a static blog are done without a server by borrowing GitHub Discussions
  • The touch points are two places on GitHub (Discussions, app permissions) and four Vercel environment variables
  • NEXT_PUBLIC_ values are build-time values, so a redeploy is required