How to build an article progress indicator in React
Today, we're going to build an article progress indicator, also known as a reading position indicator. These can be useful to allow readers to easily visualise the length of an article, and see their reading progress (more on this later).
Table of contents
What is an article progress indicator?
There are many different implementations but the most common is a slim bar along the top of the page (as you can see above on my site).
Here's what we're going to be building today:
Why use an article progress indicator?
There are a few different uses for an article progress indicator, primarily giving users an easy visualisation of the length of an article, and their reading progress. They are also a cool little feature which doesn't take long to build.
However, the use of such progress indicators is highly contrived. Arguments against include potential confusion with websites such as YouTube that use a similar looking progress bar to indicate loading progress, and the fact that a native scrollbar serves the same purpose as a reading indicator.
Use your own judgement here and decide whether or not to implement one on your site.
Tutorial
Component and state
Let's start by creating a new component called Progress
and returning a native progress
element.
1const Progress = () => {2 return <progress />;3};
Great, now let's add a progress
state to our component and initialise it's value to 0
.
1import { useState } from "react";23const Progress = () => {4 const [progress, setProgress] = useState(0);56 return <progress />;7};
Note: Remember to import
useState
if you haven't already.
Awesome. The native progress
element has two attributes we're going to use in this tutorial.
value
which is the current progress value (our progress
state value).
max
which is the maximum progress value (100 in our case as we're using a percentage).
Let's add those to our progress
element.
1<progress value={progress} max={100} />
Next we will make our progress indicator work.
Effects
Let's create an effect using useEffect
with an empty dependency array which will run when our component mounts.
1useEffect(() => {2 // ...3}, []);
Note: Remember to import
useEffect
if you haven't already.
Now, let's create a function within our effect called calculatePercentage
which will calculate our progress percentage.
1useEffect(() => {2 const calculatePercentage = () => {3 // ...4 };5}, []);
Events
We'll also bind a scroll event to the window, and call our calculatePercentage
function which we just defined.
1useEffect(() => {2 const calculatePercentage = () => {3 // ...4 };56 window.addEventListener("scroll", calculatePercentage);7 return () => {8 window.removeEventListener("scroll", calculatePercentage);9 };10}, []);
Let's step through this code a little.
- We define our
calculatePercentage
function within our effect. - When our effect runs (on component mount), we bind a scroll event to the window.
- When our component unmounts, the cleanup function runs (which is what we return from our effect), which removes our event listener.
Great, now let's add the business logic to our calculatePercentage
function.
Functionality
1const calculatePercentage = () => {2 const scrolled = window.scrollY;34 const total = document.documentElement.scrollHeight - document.documentElement.clientHeight;5};
- We've declared a variable called
scrolled
which references how far we've scrolled on the Y axis within the window. - We've declared a variable called
total
which references the total height of the document (scrollHeight
) minus the window height (clientHeight
).
Now, our total
variable is the maximum scrollable distance of our page. This means that we can easily work out our progress percentage by dividing how far we've scrolled by our total scrollable distance and multiplying it by 100.
Finally, we will store this percentage in our progress
state.
1const calculatePercentage = () => {2 const scrolled = window.scrollY;34 const total = document.documentElement.scrollHeight - document.documentElement.clientHeight;56 const percentage = (scrolled / total) * 100;78 setProgress(percentage);9};
Now you have a functional article progress indicator within your React application. Your final component should look like this:
1import { useEffect, useState } from "react";23const Progress = () => {4 const [progress, setProgress] = useState(0);56 useEffect(() => {7 const calculatePercentage = () => {8 const scrolled = window.scrollY;910 const total = document.documentElement.scrollHeight - document.documentElement.clientHeight;1112 const percentage = (scrolled / total) * 100;1314 setProgress(percentage);15 };1617 window.addEventListener("scroll", calculatePercentage);18 return () => {19 window.removeEventListener("scroll", calculatePercentage);20 };21 }, []);2223 return <progress value={progress} max={100} />;24};
Styling
Let's style our component to make it look a little nicer.
1progress {2 position: fixed;3 left: 0;4 top: 0;5 width: 100%;6 height: 0.5rem;7 appearance: none;8 border: none;9 background-color: transparent;10 color: black;11}1213progress::-webkit-progress-bar {14 background-color: transparent;15}1617progress::-webkit-progress-value {18 background-color: black;19}2021progress::-moz-progress-bar {22 background-color: black;23}
We disable the native progress
element styles, and give it our own. Feel free to customise values such as the height and colour to your preference.
Now simply render your component and admire your hard work!
Here's the finished product:
Conclusion
We've learnt how to build an article progress indicator in React, utilising the useState
and useEffect
hooks.
Hopefully you found this article insightful and you enjoy the fruits of your labour.