How to create a page scroll progress bar

In this quick tutorial you will learn how to create a simple scroll progress bar using React step by step.

It's supposed that you have already have your configured project. If you don't have one, I recommend install React via Create React App

Step 1: Create a component in your project directory

For example, I created <ProgressScroll> component folder. There are two files inside: the component file itself ProgressScroll.tsx and the style file progress-scroll.module.scss. As you can see I'm using TypeScript and Sass, but feel free and use any syntax and preprocessor you want, it will not affect the result.

.
└── Your awesome project/
    └── src/
        └── components/
            └── ProgressScroll/
                ├── ProgressScroll.tsx
                └── progress-scroll.module.scss

The code below is an example of an empty component. Next we will add some ✨code✨ to this file.

ProgressScroll.tsx
1import React, { ReactElement } from 'react';
2
3export const ProgressScroll = (): ReactElement => {
4  return (
5    <div></div>
6  );
7};

Step 2: Add a progress state

To display scrolling progress we need to store scroll progress somewhere. React Hook useState is ideal for it. Add a state [progress, setProgress] to your component. Don't forget to import useState.

ProgressScroll.ts
1import React, { ReactElement, useState } from 'react';
2
3export const ProgressScroll = (): ReactElement => {
4  const [progress, setProgress] = useState<number>(0);
5
6  return (
7    <div></div>
8  );
9};

The first value, progress, is our scroll progress value or how many percent did we scroll down the page. The second value, setProgress, is the function that is used to update our state.

Step 3: Add some style

Before we add magic let's style our scroll progress bar. Check the code below, there are updated markup:

ProgressScroll.ts
1import React, { ReactElement, useState } from 'react';
2
3import styles from './progress-scroll.module.scss';
4
5export const ProgressScroll = (): ReactElement => {
6  const [progress, setProgress] = useState<number>(0);
7
8  return (
9    <div className={styles.progressScroll}>
10      <div
11        className={styles.progressScroll__bar}
12        style={{ width: `${progress}%` }} />
13    </div>
14  );
15};

Of course we need to add some style:

progress-scroll.module.scss
1.progressScroll {
2    position: fixed;
3    top: 0;
4    z-index: 10000;
5    width: 100%;
6    height: 5px;
7    background: LightGray;
8
9    &__tracker {
10      height: 100%;
11      background-color: DarkViolet;
12      transition: 0.5s width;
13    }
14  }

A few notes on the style. z-index should be such that the scroll progress bar appears on top of all elements on your page. height, background and background-color can be anything you like.

If you run the component now, you will just see a gray bar at the top of the page. It's time for magic!

Step 4: Add magic

It's right around the corner. Before we added the scroll progress state. Now let's add a function that will calculate scrolling progress: handleScroll(). Also we will use useEventListener hook. It's a custom hook that attaches event listeners to DOM elements, the window, or media query lists. You can check it here. About React custom hooks you can read here.

ProgressScroll.ts
1import React, { ReactElement, useState } from 'react';
2import useEventListener, { Event } from 'src/hooks/useEventListener';
3
4import styles from './progress-scroll.module.scss';
5
6export const ProgressScroll = (): ReactElement => {
7  const [progress, setProgress] = useState<number>(0);
8
9  const handleScroll = (): void => {
10    const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
11    const scrolledHeight = window.scrollY;
12    const scrolledPercent = Math.round((scrolledHeight * 100) / height);
13
14    setProgress(scrolledPercent);
15  };
16
17  useEventListener(Event.SCROLL, handleScroll);
18
19  return (
20    <div className={styles.progressScroll}>
21      <div
22        className={styles.progressScroll__bar}
23        style={{ width: `${progress}%` }} />
24    </div>
25  );
26};

We will receive via DOM Event Listener in handleScroll() an information about scroll height and client height after each scroll. Here is difference between scroll height and client height:

Then using the formula above we calculate the scrolling progress. If you want to see the final result just look at the top of this page and scroll up and down.

Read more

  1. React Create App
  2. React Hooks
  3. useEventListener
  4. What is scroll height
  5. What is client height

Thanks for reading!