Well hey there đ
In this blog post Iâm going to discuss how to add user reactions to your Gatsby blog posts using a fun, open-source React UI component called react-svg-bubble-slider.

Demo
If youâre a keen bean and would like to see a fully finished demo blog Iâve created an example of how react-svg-bubble-slider could be used to add reactions to a Gatsby blog, using Fauna for the backend and Netlify Functions and apollo-server-lambda to handle the data requests.
- Demo Blog: https://netlify-reactions.netlify.app/
- Source Code: https://github.com/PaulieScanlon/netlify-reactions
Why Reactions?
Youâre probably familiar with reactions from social media platforms such as Twitter, Instagram, et al., but I rarely see reactions in blog posts. A common approach in blogs is to implement comments, but there are additional complications involved when allowing users to write whatever they want and post it to your backend.
When you expose an input, youâre giving the reader the opportunity to write anything â itâs only a matter of time before someone writes something rude. You probably donât want your blog posts peppered with expletives, so you need to set up a moderation queue to review comments and approve or delete them.
Adding reactions removes this extra overhead because youâre only allowing readers to choose from a pre-approved set of reactions. Itâs still interactive, but no moderation is required!
When I started to think about reactions I was struggling to think of a fun and original way to present the options to the reader. I toyed around with the âheartâ, âthumbs upâ / âthumbs downâ approach for a while but wasnât really excited by it, until I saw this tweet from Chris Gannon.
Todayâs interactive animation from 2016 is Emoji Bubble Slider. Itâs dynamically built and the emojis are âgluedâ together using the goo filter effect. I have licensed this a surprising number of times! #animationAtHome pic.twitter.com/vW9OlLkYWw
â Chris Gannon (@ChrisGannon) May 15, 2020
As soon as I saw Chrisâs SVG Bubble Slider, I knew it would make a perfect way to expose a pre-defined set of reactions to allow readers to leave their reactions on a blog post.
I explained the idea to Chris and he was on board. However this can only be used on free projects. If you want to use it for a commercial project, youâll need to talk to Chris about a license.
This post will broadly explain the steps required to add react-svg-bubble-slider to a Gatsby blog, but you could similarly add this to any React application.
Get Started
To get started, install the package via npm (or yarn).
npm install react-svg-bubble-sliderYouâll then need to locate your âpostsâ layout. In this Gatsby project this is saved in src/layouts/post-layout.js. Youâll need to import SvgBubbleSlider to the template or layout responsible for handling your posts.
Once youâve decided which layout youâll be amending, import SvgBubbleSlider and add it to the componentâs returned JSX.
import React from 'react'import { SvgBubbleSlider } from 'react-svg-bubble-slider'
const PostLayout = () => { return ( <div> <SvgBubbleSlider /> </div> )}
export default PostLayoutBy default SvgBubbleSlider displays 14 reactions. This is because I wanted to honor Chris Gannonâs original SVG Bubble Slider, but in a real project you may want to use a restricted set of reactions.
To modify the icons your implementation of SvgBubbleSlider displays you can use the icons prop and pass in only the icons you wish to use.
Hereâs an example:
import React from 'react'import { SvgBubbleSlider } from "react-svg-bubble-slider"
const ICONS_TO_USE = ["angry", "sad", "neutral", "smile", "happy", "cool"]
const PostLayout = () => {
return ( <div> <SvgBubbleSlider icons={ICONS_TO_USE} /> </div> )}
export default PostLayoutThere are a couple caveats when using the icons prop:
- Youâll need at least three icons. This is to ensure there are icons to âslideâ between.
- The icon names must be from the 14 icons included in the package. Check the docs for available icon names.
Get the current reaction
Now that youâve got the icons configured, youâll need to get the current reaction, show the reader the current reaction, and then provide a way for the reader to post the current reaction to your backend of choice.
You can access the current reaction via a render prop which you can pass on to a button.
{% raw %} import React from 'react' import { SvgBubbleSlider } from 'react-svg-bubble-slider'
const ICONS_TO_USE = ["angry", "sad", "neutral", "smile", "happy", "cool"]
const PostLayout = () => { return ( <div>- <SvgBubbleSlider icons={ICONS_TO_USE} />+ <SvgBubbleSlider icons={ICONS_TO_USE}>+ {({ reaction }) => (+ <div+ style={% raw %}{{+ display: "flex",+ justifyContent: "center",+ margin: "24px 0px",+ }}{% endraw %}+ >+ <button onClick={() => console.log(reaction)}>+ {reaction ? reaction : "?"}+ </button>+ </div>+ )}+ </SvgBubbleSlider> </div> ) }
export default PostLayout{% endraw %}Now that you have a method to access the current reaction, you can do something useful with it. In this example, Iâll show you how to increment the reaction count each time the button has been clicked and store it in Reactâs local state.
In reality, youâll need to store this data in your backend of choice, but to get you started this will at least show how the React side of things could work.
Import useState so you can store the count value in Reactâs local state, then set an initial shape of the data along with a initial value for the counts.
{% raw %}- import React from 'react'+ import React { useState } from 'react' import { SvgBubbleSlider } from "react-svg-bubble-slider"
const ICONS_TO_USE = ["angry", "sad", "neutral", "smile", "happy", "cool"]
const PostLayout = () => {
+ const [stateReactions, setStateReactions] = useState(+ ICONS_TO_USE.map((name) => {+ return {+ name: name,+ count: 0,+ }+ })+ )
return ( <div> <SvgBubbleSlider icons={ICONS_TO_USE}> {({ reaction }) => ( <div style={% raw %}{{ display: "flex", justifyContent: "center", margin: "24px 0px", }}{% endraw %} > <button onClick={() => console.log(reaction)}> {reaction ? reaction : "?"} </button> </div> )} </SvgBubbleSlider> </div> ) }
export default PostLayout{% endraw %}If you pop in a console.log(stateReactions) you should see an array of objects like the below:
[ { name: "angry", count: 0, }, { name: "sad", count: 0, }, { name: "neutral", count: 0, }, { name: "smile", count: 0, }, { name: "happy", count: 0, }, { name: "cool", count: 0, },]Weâre now going to surface the icons and their corresponding counts by mapping over the array shown above and returning an SvgIcon and its count value.
{% raw %} import React { useState } from 'react'- import { SvgBubbleSlider } from "react-svg-bubble-slider"+ import { SvgBubbleSlider, SvgIcon } from "react-svg-bubble-slider"
const ICONS_TO_USE = ["angry", "sad", "neutral", "smile", "happy", "cool"]
const PostLayout = () => { const [stateReactions, setStateReactions] = useState( ICONS_TO_USE.map((name) => { return { name: name, count: 0, } }) )
return ( <div> <SvgBubbleSlider icons={ICONS_TO_USE}> {({ reaction }) => ( <div style={% raw %}{{ display: "flex", justifyContent: "center", margin: "24px 0px", }}{% endraw %} > <button onClick={() => console.log(reaction)}> {reaction ? reaction : "?"} </button> </div> )} </SvgBubbleSlider>+ <div+ style={% raw %}{{+ display: "grid",+ gridTemplateColumns: "repeat(6, 48px)",+ justifyContent: "center",+ }}{% endraw %}+ >+ {stateReactions.map((reaction, index) => {+ const { name, count } = reaction+ return (+ <div+ key={index}+ style={% raw %}{{+ alignItems: "center",+ display: "flex",+ flexDirection: "column",+ }}{% endraw %}+ >+ <SvgIcon name={name} />+ <div>{count}</div>+ </div>+ )+ })}+ </div> </div> ) }
export default PostLayout{% endraw %}The last step is to handle the user interaction when the button is clicked.
For this youâll need to pass the current reaction name to a function and then update itâs count value in local state.
Inside the handleReaction youâll map over the current state values and on each iteration check if the reaction name matches the currentReaction. If so, increment the count value; if not, return the reaction object untouched.
This operation updates state using setStateReactions and each time an update occurs the JSX will re-render. You should see the increased count value appear under the corresponding SvgIcon.
{% raw %} import React { useState } from 'react' import { SvgBubbleSlider, SvgIcon } from "react-svg-bubble-slider"
const ICONS_TO_USE = ["angry", "sad", "neutral", "smile", "happy", "cool"]
const PostLayout = () => {
const [stateReactions, setStateReactions] = useState( ICONS_TO_USE.map((name) => { return { name: name, count: 0, } }) )
+ const handleReaction = (currentReaction) => {+ setStateReactions(+ stateReactions.map((reaction) =>+ reaction.name === currentReaction+ ? {+ ...reaction,+ count: (reaction.count += 1),+ }+ : reaction+ )+ )+ }
return ( <div> <SvgBubbleSlider icons={ICONS_TO_USE}> {({ reaction }) => ( <div style={% raw %}{{ display: "flex", justifyContent: "center", margin: "24px 0px", }}{% endraw %} >- <button onClick={() => console.log(reaction)}>+ <button onClick={() => handleReaction(reaction)} {reaction ? reaction : "?"} </button> </div> )} </SvgBubbleSlider> <div style={% raw %}{{ display: "grid", gridTemplateColumns: "repeat(6, 48px)", justifyContent: "center", }}{% endraw %} > {stateReactions.map((reaction, index) => { const { name, count } = reaction return ( <div key={index} style={% raw %}{{ alignItems: "center", display: "flex", flexDirection: "column", }}{% endraw %} > <SvgIcon name={name} /> <div>{count}</div> </div> ) })} </div> </div> ) }
export default PostLayout{% endraw %}Remember: this example only stores the count values in local state. If you refresh your browser, all values are reset. In a real example these count values will need to be stored in a database.
In the demo application Iâve used FaunaDB to store reaction counts. If youâd like to learn Fauna, Chris Biscardi has an excellent guide to Fauna that walks through everything youâll need to get up and running.
If youâre using react-svg-bubble-slider in your project Iâd love to hear from you on Twitter (@PaulieScanlon)!!


