DEV Community

Ata Parvin Ghods
Ata Parvin Ghods

Posted on • Updated on

React progress bar on page load/route change(both Next js & CRA)

Last day I was working on a project which I had to add progress bar (Like youtube's) but I couldn't find anything useful on the internet.

So i decided to create my own and wanna share it with you.
Hope it helps!

//output
progress-bar-ata-parvin-ghods

I'm going to start with create-react-app

// Create a new app
npx create-react-app progress-app react-router-dom react-topbar-progress-indicator

// Run the created app
cd progress-app
yarn start

// http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

I used react-topbar-progress-indicator package but you can use/create your own.

1.Define your routes in App.js
(src/App.js)

import { BrowserRouter, Switch, Route } from "react-router-dom"

const App = () => {
   return (
      <>
         <BrowserRouter>
            <Switch>
               <Route exact path='/' />
               <Route exact path='/about' />
            </Switch>
         </BrowserRouter>
      </>
   )
}

export default App
Enter fullscreen mode Exit fullscreen mode

2.Let's create some pages components
(src/pages/Home.js)

import { Link } from "react-router-dom"

const Home = () => {
   return (
      <div>
         <h1>Home page</h1>
         <Link to='/about'>About</Link>
      </div>
   )
}

export default Home
Enter fullscreen mode Exit fullscreen mode

(src/pages/About.js)

import { Link } from "react-router-dom"

const About = () => {
   return (
      <div>
         <h1>About page</h1>
         <Link to='/'>Home</Link>
      </div>
   )
}

export default About
Enter fullscreen mode Exit fullscreen mode

3.Import pages in App.js

const App = () => {
   return (
      <>
         <BrowserRouter>
            <Switch>
               <Route exact path='/' component={Home} />
               <Route exact path='/about' component={About} />
            </Switch>
         </BrowserRouter>
      </>
   )
}
Enter fullscreen mode Exit fullscreen mode

4.Now we will create a component and surround our Routes in it.
(src/CustomSwitch.js)

const CustomSwitch = ({ children }) => {
   return (
      <Switch>
         { children }
      </Switch>
   )
}
Enter fullscreen mode Exit fullscreen mode

This will return Routes in Switch Component.
Now out App.js should be look like this

const App = () => {
   return (
      <>
         <BrowserRouter>
            <CustomSwitch>
               <Route exact path='/' component={Home} />
               <Route exact path='/about' component={About} />
            </CustomSwitch>
         </BrowserRouter>
      </>
   )
}
Enter fullscreen mode Exit fullscreen mode

5.In our CustomSwitch component

import React, { useEffect, useState } from "react"
import { Switch, useLocation } from "react-router-dom"
import TopBarProgress from "react-topbar-progress-indicator"

const CustomSwitch = ({ children }) => {
   const [progress, setProgress] = useState(false)
   const [prevLoc, setPrevLoc] = useState("")
   const location = useLocation()

   return (
      <>
         {progress && <TopBarProgress />}
         <Switch>{children}</Switch>
      </>
   )
}
Enter fullscreen mode Exit fullscreen mode

We use react-router-dom location hook. This hook will show us the path.

   useEffect(() => {
      setPrevLoc(location.pathname)
      setProgress(true)
      if(location.pathname===prevLoc){
          setPrevLoc('')
      }
   }, [location])

   useEffect(() => {
      setProgress(false)
   }, [prevLoc])
Enter fullscreen mode Exit fullscreen mode

Whenever location changed, first useEffect hook will run and change the previous location & set progress bar to true.
And whenever previous location changed the second useEffect will run and change progress bar back to false.

Our CustomeSwitch.js should look like this
(src/CustomSwitch.js)

import React, { useEffect, useState } from "react"
import { Switch, useLocation } from "react-router-dom"
import TopBarProgress from "react-topbar-progress-indicator"

const CustomSwitch = ({ children }) => {
   const [progress, setProgress] = useState(false)
   const [prevLoc, setPrevLoc] = useState("")
   const location = useLocation()

   useEffect(() => {
      setPrevLoc(location.pathname)
      setProgress(true)
      if(location.pathname===prevLoc){
          setPrevLoc('')
          //thanks to ankit sahu
      }
   }, [location])

   useEffect(() => {
      setProgress(false)
   }, [prevLoc])

   return (
      <>
         {progress && <TopBarProgress />}
         <Switch>{children}</Switch>
      </>
   )
}

export default CustomSwitch
Enter fullscreen mode Exit fullscreen mode

And your done with create-react-app

Let's continue with Next.Js
This one is actually quite simple than CRA

Create Next app using commands

// Create a new app
npx create-next-app progress-app react-topbar-progress-indicator

// Run the created app
cd progress-app
yarn dev

// http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

1.Add one page
(pages/about.js)

import Link from "next/link"

const About = () => {
   return (
      <div>
         <h1>About page</h1>
         <Link href='/'>
            <a>HOME PAGE</a>
         </Link>
      </div>
   )
}

export default About
Enter fullscreen mode Exit fullscreen mode

And your index.js
(pages/index.js)

import Link from "next/link"

const Home = () => {
   return (
      <div>
         <h1>Home page</h1>
         <Link href='/about'>
            <a>About PAGE</a>
         </Link>
      </div>
   )
}

export default Home
Enter fullscreen mode Exit fullscreen mode

Now we are ready

3.In _app.js
(pages/_app.js)

import Router from "next/router"
import { useState } from "react"

export default function MyApp({ Component, pageProps }) {
   const [progress, setProgress] = useState(false)

   return (
      <Component {...pageProps} />
   )
}
Enter fullscreen mode Exit fullscreen mode

Next.Js provide us some functions with Router, which you can read more about it in Next-JS-Routing

import Router from "next/router"
import { useState } from "react"
import TopBarProgress from "react-topbar-progress-indicator"


export default function MyApp({ Component, pageProps }) {
   const [progress, setProgress] = useState(false)

   Router.events.on("routeChangeStart", () => {
      setProgress(true) 
      //function will fired when route change started
   })

   Router.events.on("routeChangeComplete", () => {
      setProgress(false) 
      //function will fired when route change ended
   })

   return (
      <>
         {progress && <TopBarProgress />}
         <Component {...pageProps} />
      </>
   )
}
Enter fullscreen mode Exit fullscreen mode

When Route changed, state become true and progress bar will be shown and when route changing ended it will disappear.

You are done my friends!

I hope you enjoyed this post.

Oldest comments (5)

Collapse
 
lordvic4real profile image
lordvic4real • Edited

thank you so much, it worked, so straight forward, for the most basic learner to graps... please how do i include spinner at the top right edge of the progress bar

Collapse
 
ataparvinghods profile image
Ata Parvin Ghods • Edited

Thank you so much. Glad you like it. I didn't get exactly what you want about spinner? If you want spinner instead of progress bar you can change it in the component and add spinner component (instead of TopBarProgress add Spinner ) .

Collapse
 
ankitsahu01 profile image
Ankit Sahu

Awesome. But there is a little problem, when we click on same link twice then top progress bar starts and will never stop.
This is happening bcoz the current location and previous location are same and thats by the useEffect with dependency prevLoc not invoking.
To get rid of it ---->

useEffect(() => {
setPrevLoc(location.pathname);
setProgress(true);
if(location.pathname===prevLoc){
setPrevLoc(''); //To fix same path infinite progress bar loading
}
}, [location]);

Collapse
 
ataparvinghods profile image
Ata Parvin Ghods • Edited

Thank you so much ankit!
I edited the post, much appreciated ❤

Collapse
 
ankitsahu01 profile image
Ankit Sahu • Edited

Most Welcome... one more thing is that the code which i wrote above having a Warning -> React Hook useEffect has a missing dependency: 'prevLoc'

To get rid of it simply modify few more things --->

const [progress, setProgress] = useState(false);
const [prevLoc, setPrevLoc] = useState({});
const location = useLocation();

useEffect(() => {
setPrevLoc(location);
setProgress(true);
window.scrollTo(0, 0); //Go to top on every page load
}, [location]);

useEffect(() => {
setProgress(false);
}, [prevLoc]);