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!
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
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
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
(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
3.Import pages in App.js
const App = () => {
return (
<>
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/about' component={About} />
</Switch>
</BrowserRouter>
</>
)
}
4.Now we will create a component and surround our Routes in it.
(src/CustomSwitch.js)
const CustomSwitch = ({ children }) => {
return (
<Switch>
{ children }
</Switch>
)
}
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>
</>
)
}
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>
</>
)
}
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])
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
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
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
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
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} />
)
}
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} />
</>
)
}
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.
Top comments (5)
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]);
Thank you so much ankit!
I edited the post, much appreciated ❤
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]);
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
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 ) .