Add comments to your blog using GitHub Issues

Easily add comments to your blog based on github issues using Utterances. This article explains how to add it in a react based site (e.g. Gatsby).

Code
September 18th, 2020
4 min read

Comments

Having comments on your personal blogs can help you interact with your readers, and make them feel heard too. They can make your blog posts feel alive, rather than just a one-way interaction.

Of course, having comments in your blog may have some downsides, such as spammy and offensive comments from some trolls. Therefore, it is not advisable to have a very open comment system without a user login feature to deter trolls and bots.

I've seen some people use Disqus, which has a few pros and cons (such as ads or price), and I've also seen some developers build their own firebase-based solutions. However, I wanted to have something simple and free that would also look nice.

I read about Utterances from a blog post by Tania Rascia, who has a brilliant blog that I highly recommend, and I was quite impressed with how easy it is to implement it in my newly built site quickly. It took me a total of 20 minutes to integrate it with my Gatsby blog, so I'll be explaining in this article the steps to follow so you can also add it to your blog easily.

What is Utterances?

It's a free open source app, that will work as a widget in your blog using GitHub issues as the blog comments storage.

It will create a new issue for new posts and put any comments on your posts as comments on that issue itself. You can edit your comment from that GitHub issue too. It's pretty neat.

Is Utterances right for my blog?

Since Utterances stores the comments in GitHub issues, this post assumes that your blog is targeting an audience of developers who would generally have a GitHub account.

Also, if your blog is super popular with high traffic, using this solution might not work for you because the widget might hit the GitHub API rate limit and your comment feature will not work all the time, so keep this in mind for the future.

How does it look like?

Just scroll down to the end of the page to try it out. You can view the comments and issues here.

Adding Utterances to your blog

Adding Utterances in a standard Html-Js website is a matter of merely adding a simple script. The utterances website documents the easy steps to do this.

// example of embedding this in a non-react app

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

However, this walkthrough will be about adding Utterances in a react based blog, such as Gatsby or Next.

Here is a summary of the steps to follow:

  1. Setting up the public repository in GitHub.
  2. Install the utterances GitHub app onto the repo.
  3. Add a react component for the comment.
  4. Add a useEffect in the posts component to render the widget.

Step 1: Setting up the public repository in GitHub

You have two options, use an existing repo, or create a new empty repo. Either way, it has to be a public repo. Creating a new repo is better to avoid polluting the issues of your code repo with the comments (though you can add labels to the comments).

In this comments repository, you can also commit a utterances.json file and add the domain of your website. This will prevent other unauthorized websites using your comments repo, which can happen if someone clones your website and forgets to change the utterances script (which we will add below in step 4).

utterances.json
{
  "origins": [
    "https://yourDomain.tld"
  ]
}

Note: The above file gets committed in the same repository you are using for the comments, not in your blog repository.

Step 2: Install the utterances GitHub app onto the repo

After you have decided which repo you will use, we can go to the utterances GitHub app to install it on our repository. Just give the app the necessary permissions to read and write to the selected repository only, and we can move on to step 3.

Step 3: Create a react component for the comments container

Now, that we have everything ready, let's create a new component in our blog that we can then use wherever we want to integrate our comments.

components/Comment.js
import React from 'react'

const Comment = React.forwardRef((props, commentBox) => {
  return <div ref={commentBox} className="comments" />
});

export default Comment;

So, we are simply creating an empty div here but what matters is that we are forwarding the reference ref={commentBox} to this div. React forwarding refs allow us to obtain a ref to the div DOM element, which we will use in the step below.

Step 4: Use the Comments component in our posts template

In this last step, we will need to integrate the Comment component in the place where we want the comments to show. In this example, we will use it in the Post Footer Component. You can directly use it in the post-template file, or anywhere you want.

In a rush: Jump to the end for the final code of this step.

Let's create a ref first in our parent component PostFooter.js.

PostFooter.js
import React from 'react'

const PostFooter = () => {

    const commentBox = React.createRef();

    return (
        // ...
    )
}

export default PostFooter;

After that, we will be adding a useEffect to create the script element on component mount. I've added some comments to explain the value for each attribute.


useEffect(() => {
  const commentScript = document.createElement('script')

  commentScript.async = true
  commentScript.src = 'https://utteranc.es/client.js'

  // define the name of the repository you created here as 'owner/repo'
  // or import it from your config file if you have one.
  commentScript.setAttribute('repo', siteConfig.commentsRepo)

  // define the blog post -> issue mapping (e.g. page pathname, page url).
  // here the issues will be created with the page pathname as the issue title.
  commentScript.setAttribute('issue-term', 'pathname')

  // define a custom label that you want added to your posts.
  commentScript.setAttribute('label', 'blog-comment')

  // define if you want to use dark or light theme.
  commentScript.setAttribute('theme', 'github-light')

  commentScript.setAttribute('crossorigin', 'anonymous')

  // we will append this script as a child to the ref element we have created above
  if (commentBox && commentBox.current) {
      commentBox.current.appendChild(commentScript)
    } else {
      console.log(`Error adding utterances comments on: ${commentBox}`)
   }

  }, [])

If you have a dark/light theme toggler in your blog, you can still modify this further to allow changing the theme based on your user preference. Let's bring in the theme context which should be implemented somewhere else in your site (I won't go in much detail now on how to implement the theme context, as it's not relevant to this post).


  const { theme } = useThemeContext();

  useEffect(() => {

    const commentsTheme = theme && theme === 'dark'
            ? 'github-dark'
            : 'github-light'

    commentScript.setAttribute('theme', commentsTheme)

   // ...

 }, [theme])

Notice that we added theme as the second argument to the useEffect function. This allows triggering the useEffect function every time the user toggles the theme.

However, there is a catch, as we still need to implement a cleanup on this useEffect(); otherwise, we will end up with multiple comment boxes every time the theme is toggled. To clean up, we need to return a function in the useEffect function, and in that function, we can simply remove the utterances elements from the dom.


  useEffect(() => {

  // ...

  const removeScript = () => {
       commentScript.remove();
       document.querySelectorAll(".utterances").forEach(el => el.remove());
   };

  // clean up on component unmount
   return () => {
     removeScript();
   };

 }, [theme])

Finally, the last step is to render the Comment component we created in step 3 and pass it the commentBox ref.

    ...

    return (
        <>
          <Comment ref={commentBox} />
        </>
      )

    ...

Now you can use the PostFooter component in your post-template or on your page directly.

Final code for step 4

Here is the final cleaned up code for the PostFooter.js file.

PostFooter.js

import React from 'react'
import Comment from './components/Comment'
import {useThemeContext} from '../context/theme-context';
import {siteConfig} from '../utils/site-config';

const PostFooter = () => {

  const commentBox = React.createRef();

  const { theme } = useThemeContext();

  useEffect(() => {
    const commentScript = document.createElement('script')
    const commentsTheme = theme && theme === 'dark'
            ? 'github-dark'
            : 'github-light'
    commentScript.async = true
    commentScript.src = 'https://utteranc.es/client.js'
    commentScript.setAttribute('repo', siteConfig.commentsRepo)
    commentScript.setAttribute('issue-term', 'pathname')
    commentScript.setAttribute('id', 'utterances')
    commentScript.setAttribute('label', 'comment')
    commentScript.setAttribute('theme', commentsTheme)
    commentScript.setAttribute('crossorigin', 'anonymous')


    if (commentBox && commentBox.current) {
      commentBox.current.appendChild(commentScript)
    } else {
      console.log(`Error adding utterances comments on: ${commentBox}`)
    }

    const removeScript = () => {
      commentScript.remove();
      document.querySelectorAll(".utterances").forEach(el => el.remove());
    };

    return () => {
      removeScript();
    };

  }, [theme])

  return (
    <>
      <Comment ref={commentBox} />
    </>
  )
}

export default PostFooter;

With that implemented, you should now have a commenting feature integrated within your blog. This should be all.

Thank you for reading. I hope you found this post useful.

Blog listing photo: circle icons PNG Designed By flat**** from Pngtree.com