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!