How to add comment support on your Gatsby blog using Github utterances

29th July 2020

creativcoder / 6min


This post is about integrating comments support in your blog using a free and open source service.

  • Update (2022): Prefer using Giscuss instead.

cover image

Having a way to engage with your audience is probably the next step you can take if your blog is helping others in some way. Back when I had used hugo as my static site generator, I had used Disqus, which is a comments service/system. But as with ordinary third party services, Disqus is notorious for putting their own ads and can get quite slow. As a fan of open source and code reuse, I knew there had to be some open source comments service out there. After a day of sifting through the interwebs, I found a solution that I think for the time being serves my purpose of engaging with my audience.

What follows is a quick guide on how to integrate Github's utterances comments system into your blog. This also became an excuse for me stay updated with all the recent changes in the web dev space, as I've been mostly doing backend for the last two years.

Utternaces is a lightweight comments widget built on top of Github issues. Some of its highlights are:

  • Open source. 🙌

  • No tracking, no ads, always free. 📡🚫

  • No lock-in. All data stored in GitHub issues. 🔓

  • Styled with Primer, the css toolkit that powers GitHub. 💅

  • Dark theme. 🌘

  • Lightweight. Vanilla TypeScript. No font downloads, JavaScript frameworks or polyfills for evergreen browsers. 🐦🌲

Target audience: You know ReactJS, and you probably know better than me and maybe can provide suggestions on how I can improve the integration.

Disclaimer: This comments system only works if your target audience has a GitHub account, which in majority of cases are developers. As my blog is mostly oriented towards developers, this aligns perfectly with my blog.

The official guide of utterances mentions integrating the comments widget on a vanilla HTML website. That won't work for my case as I happen to be running a site that is generated by GatsbyJS, which I think is a really efficient static site generator not only in terms of development experience but also in terms of performance of the generated sites compared to my past experiences with static site generators such as Hugo.

Gatsby uses React under the hood which meant that I needed to integrate utterances as a React component.

Note: I am by no means an expert on react or even a frontend expert, but the following steps is me just sifting through bunch of documentation pages and tying to cobble together a working solution.

So here's how I did it.

Installing utterances

First, I setup a Github repository (has to be a public repository) that will act as a placeholder for issues to be created within: https://github.com/creativcoder/creativcoder.dev-comments This is needed if you have a website that is not running as a Github pages site. If you happen to have your website as a Github repository, you can configure the same repository for utterances.

Next, we go to: https://github.com/apps/utterances page. You will see an 'Install' button in green on the right. Clicking that, you will need to provide the repository where you want to install the widget. I chose my comments repository above.

Post installation, you will be redirected to this page. In the 'Enabling Utterances' section, we need to place the following script in a div tag (or any container tag) where we want our comment widget to appear:

<script src="https://utteranc.es/client.js"
        repo="[ENTER REPO HERE]"
        issue-term="pathname"
        theme="github-light"
        crossorigin="anonymous"
        async>
</script>

As you can see, the script tag above has multiple attributes that can be configured. The important ones to look at are:

  • repo: should point to the repository you installed utterances above section.
  • issue-term: how utterances should match the issues with posts on a website. In my case, am using the title of the posts. You can refer to 'Blog Post to Issue Mapping' section in https://utteranc.es/ for more details and choose as per your preference.

Integrating the comment widget

Now as I said above, the script needs to be put in a container tag for us to render the comments widget. That translates to having a component in react, within which we'll append this script tag programmatically whenever a blog post page is rendered. This will need to happen conditionally as not all pages are about a blog post page, such as the index page and the about page.

To have a container for the widget to be created within, I first created a Comment react component (a functional component) in components/comment.js:

// commment.js

import {useRef, useEffect} from 'react'
import React from 'react';
import { useStaticQuery, graphql } from 'gatsby'

const Comment = ({commentBox}) => (<div ref={commentBox} className="comments"></div>)
export default Comment

It's a very trivial component. The only thing of intereset here is the commentBox react ref. React Refs provide a way to access DOM nodes or React elements created within a component's render method. We'll see how that is used later. It's not really recommended to use refs, as they are sort of an escape hatch but since we only have one use case for it here, we should be fine.

Next, I have a <Posts> component in my blog which renders each blog post. This is where I want the comment widget to appear at the end of each post. I needed a way to render the Comments component conditionally. To achieve that, I relied on the excerpt prop which is true if the rendered page is an index page, where only an excerpt of all blog posts are shown. In other cases it's set to false. We can use that to conditionally render the Comments component like so:

// components/posts.js
{ excerpt? <></>: <Comment commentBox={commentBox} />}

It's the usual ternary operator. The <></> syntax are fragments in react, which is empty in the true branch as we only want to render the widget when the user in on the blog post page. We also see that the comment widget has a commentBox prop being passed. This is how we gain access to the Comment component for use within the Posts component. We'll use this ref to append the comment script tag.

Back in my Posts component, I create a ref that was attached to the commentBox prop on Comment component:

// components/post.js
const commentBox = React.createRef()

Then, I use the useEffect hook to add the script tag to the Comment component via the ref:

  const commentBox = React.createRef()

  useEffect(() => {
    const scriptEl = document.createElement('script')
    scriptEl.async = true
    scriptEl.src = 'https://utteranc.es/client.js'
    scriptEl.setAttribute('repo', 'creativcoder/creativcoder.dev-comments')
    scriptEl.setAttribute('issue-term', 'title')
    scriptEl.setAttribute('id', 'utterances')
    scriptEl.setAttribute('theme', 'github-light')
    scriptEl.setAttribute('crossorigin', 'anonymous')
    if (commentBox && commentBox.current) {
      commentBox.current.appendChild(scriptEl)
    } else {
      console.log(`Error adding utterances comments on: ${commentBox}`)
    }
  }, [])

The code in useEffect will run everytime a Post component is rendered which will then append the comment widget to each blog post page. You can see that set the same script attributes are being added but with javascript. We then finally append it to the commentBox ref via its current property which is active when it is rendered.

With that done, I did just did a yarn deploy and what you see below is our shiny new comment widget 👇. Hope you find this post helpful. Let me know if you have any issues setting it up in the comments below.

Until next time!


Want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please follow Rust's code of conduct. This comment thread directly maps to a discussion on GitHub, so you can also comment there if you prefer.

;