How to use Utterances with React
Ahoy! In this post Iâll be explaining how I used the fantastic Utteranc.es đŽ with React.
On the below links youâll find an example site built with Gatsby along with a preview, and for good measure Iâve also included a link to the âcomments repoâ which Iâve used to capture issues/comments.
Example
What are Utterances?
Iâve lifted this straight from https://utteranc.es/
A lightweight comments widget built on GitHub issues. Use GitHub issues for blog comments, wiki pages and more!
This sounds like the perfect solution for anyone wishing to quickly add comments to a site, and if you look at whatâs required to get going, itâs pretty straightforward.
<script
src='https://utteranc.es/client.js'
repo='[ENTER REPO HERE]'
issue-term='pathname'
theme='github-light'
crossorigin='anonymous'
defer
></script>
Adding the Utterances Script
But⌠in React adding a script element isnât quite that easy. If you were to use the above in React nothing would
happen. To note: youâd also see a warning about crossorigin
needing to be crossOrigin
, but that aside, the
Utterances script wouldnât load, and no issues/comments would be returned and rendered on the page.
React and Scripts
Firstly, React uses innerHtml
to make changes to the DOM and because of this, and inline with the
HTML spec, browsers must
not execute scripts tags set via the use of innerHTML
. Secondly, each time a component mounts or a re-render occurs
due to a state change, React would be adding and executing this script over and over again, đĽ´.
React Hooks and Scripts
Luckily however, React does provide a bit of escape hatch. When using one of Reactâs life cycle methods useEffect
with
an empty dependency array, you are able to perform an action only once.
Hereâs an âonly onceâ example.
When the component mounts itâll fire off the console.log
and any other changes triggered by state wonât trigger this
âside effectâ.
// src/components/utterances-comments.js
import React, { useEffect } from 'react';
const UtterancesComments = () => {
useEffect(() => {
console.log('only once');
}, []);
return null;
};
export default UtterancesComments;
Using this same technique you can manually create a script tag using good old fashioned vanilla JavaScript and then manually append it to a DOM element wherever you like.
React Hooks and Utterances
In the below snippet Iâm using the same âonly onceâ useEffect
to create a new script element and append it to a div
that is returned by Jsx.
// src/components/utterances-comments.js
import React, { useEffect, useRef } from 'react';
const UtterancesComments = () => {
const ref = useRef();
useEffect(() => {
const script = document.createElement('script');
const config = {
src: 'https://utteranc.es/client.js',
repo: '[ENTER REPO HERE]',
'issue-term': 'pathname',
theme: 'github-light',
crossOrigin: 'anonymous',
defer: true,
};
Object.entries(config).forEach(([key, value]) => {
script.setAttribute(key, value);
});
setTimeout(() => {
ref.current.append(script);
}, 300);
}, []);
return <div ref={ref} />;
};
export default UtterancesComments;
Utterances Component
Thereâs a few things going on in the above snippet, so lemme talk you through my approach. You can find the src
for
the complete component here:
utterances-comments.js.
The completed component is pretty much the same as the below, but Iâve added a few props
, defaultProps
and
PropTypes
to make it a little easier to configure and safer to use in production.
useRef
Iâm using useRef
from React to store a reference to the div element that Iâll later return in Jsx, more on that in a
moment.
const ref = useRef();
createElement
Nothing more than good old fashioned vanilla JavaScript to create a new const
called script
and the creation of a
new script HTML element.
const script = document.createElement('script');
config
This object is used to hold key value pairs for the attributes that will be added to the newly created HTML script element. The docs do a really good job at explaining what all the config options are, theyâre interactive too, so I wonât dive any deeper into that here.
const config = {
src: 'https://utteranc.es/client.js',
repo: '[ENTER REPO HERE]',
'issue-term': 'pathname',
theme: 'github-light',
crossOrigin: 'anonymous',
defer: true,
};
setAttribute
Iâve used
Object.entries to
âloopâ over each key in the config
object and have destructured both the key
and the value
. Pop a console.log
in
and try it yourself.
Object.entries(config).forEach(([key, value]) => {
console.log(key, value);
script.setAttribute(key, value);
});
This would result in something similar to the below.
src https://utteranc.es/client.js
utterances-comments.js:20 repo [ENTER REPO HERE]
utterances-comments.js:20 issue-term pathname
utterances-comments.js:20 theme github-light
utterances-comments.js:20 crossOrigin anonymous
utterances-comments.js:20 defer true
I can now use the key
value
pairs to set the script attributes using
setAttribute.
The complete script
element would then look something like this.
<script
src="https://utteranc.es/client.js"
repo="[ENTER REPO HERE]"
issue-term="pathname"
theme="github-light"
crossorigin="anonymous"
defer="true"
></script>
append
And lastly I use append to, well, append the script
element to the ref
I defined earlier.
setTimeout(() => {
ref.current.append(script);
}, 300);
I did notice during testing however that if I didnât use a setTimeout
the code occasionally errored.
In my example repo Iâve used Gatsbyâs new Head API to set elements in the document head, and I wrote a little more about a common migration pattern from React Helmet in this post: How to use Gatsbyâs Head API with MDX
I should also point out that there is a node module for implementing Utterances in React, Itâs called utterances-react which, rather annoyingly I didnât discover until after Iâd scratched my head getting this to work! It hasnât been updated for a few years though, so I donât know if it still works.
Gatsby Script API
As you might have heard, Gatsby recently released the Script API, I wrote a little more about how I used it to add Google Analytics to a Gatsby site: How to use Gatsbyâs Script API with Google Analytics, butâŚ
Script API isnât quite the right tool for the job here. The main reason is that when using Script API the way I have
in the Utterances Component, it wonât allow me to append
it to a target.
Instead, Script API always adds the script element to the bottom of the HTML <body />
. This is probably what youâd
want in most cases, but you might like the ability to purposefully define where in your page the issues/comments are
displayed.
Using the ref
approach as I explained earlier allows me to have more control over the final position, or location in
the DOM of my script. Arguably, adding third-party scripts in the middle of your page isnât the best idea in case they
block more important content for loading but, #YOLO.
Community!
Iâm gonna go ahead and implement what Iâve shown here in my site for a few key reasons.
- Itâs cool! I really like Utterances, and the only way to fully take it for a test drive is to use it. My site, if nothing else is a proving ground for how well Gatsby can integrate with just about anything.
- Iâm always looking to improve my content. And whilst I appreciate the DMâs, theyâre locked away and others canât see your questions or feedback. This sometimes leads to the same question being asked multiple times and I must admit, Iâm not the best at managing my Twitter DMs.
- Iâm hoping we can all meet more folks working in this space, by leaving a comment, or asking a question, others can see you, and potentially help you better than I can.
- Promote yourself! If youâve written something similar or are working on something youâd like to share, add a link in the comments. I have no problem with self-promotion. In fact, I encourage it.
Just keep it âfamily-friendlyâ â Please!
Thanks friends, see you around!