Adding Top Loading Bar & React Router DOM in Our News App ๐ฆ๐งญ
So far our News App shows one category of articles on a single page, with a spinner that
appears while we "fetch" data. In this React JS tutorial for beginners, we'll take our
project a big step forward by adding a slim top loading bar (like the one you see on
YouTube or GitHub) for a more polished loading experience, and by using
React Router DOM to create separate routes for each news category โ Business,
Entertainment, General, Health, Science, Sports, and Technology.
In this tutorial, we will learn:
react-router-dom and react-top-loading-barBrowserRouter, Routes, and Route in App.jsNavbar component with NavLinkref from a child componentWhat is React Router DOM?
React Router DOM is the standard library for handling navigation in React
applications. It lets us map different URLs to different components, so users can
visit /business or /sports and see the matching news category โ without a full
page reload. This is what makes React apps feel like real multi-page websites while
staying fast and smooth as a Single Page Application (SPA).
What is a Top Loading Bar?
A top loading bar is that thin, colored progress bar that slides across the very top of the screen while content is loading. It's a subtle but powerful UX pattern used by sites like YouTube and GitHub โ it tells users "something is happening" without blocking the whole screen the way a spinner does.
---Step 1: Install the Required Packages
Open your terminal in the project folder and install react-router-dom for routing
and react-top-loading-bar for the progress bar.
npm install react-router-dom react-top-loading-bar
Step 2: Wrap the App with BrowserRouter
Open index.js and wrap the <App /> component with BrowserRouter so
routing works throughout the whole application.
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Step 3: Build a Navbar Component
Let's create a Navbar.js component using NavLink so users can jump between
news categories. NavLink automatically highlights the active route.
import React from "react";
import { NavLink } from "react-router-dom";
const Navbar = () => {
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<div className="container-fluid">
<NavLink className="navbar-brand" to="/">
News App
</NavLink>
<div className="collapse navbar-collapse">
<ul className="navbar-nav ms-auto">
{["business", "entertainment", "general", "health", "science", "sports", "technology"].map(
(category) => (
<li className="nav-item" key={category}>
<NavLink
className="nav-link text-capitalize"
to={`/${category}`}
>
{category}
</NavLink>
</li>
)
)}
</ul>
</div>
</div>
</nav>
);
};
export default Navbar;
Step 4: Define Category Routes in App.js
Now let's set up Routes and Route in App.js, rendering a News
component for each category with a different category prop.
import React, { Component, createRef } from "react";
import { Routes, Route } from "react-router-dom";
import LoadingBar from "react-top-loading-bar";
import Navbar from "./Navbar";
import News from "./News";
export default class App extends Component {
state = {
progress: 0,
};
topLoadingBarRef = createRef();
render() {
return (
<div>
<LoadingBar
color="#f11946"
ref={this.topLoadingBarRef}
height={3}
/>
<Navbar />
<Routes>
<Route
path="/"
element={<News topLoadingBarRef={this.topLoadingBarRef} category="general" />}
/>
<Route
path="/business"
element={<News topLoadingBarRef={this.topLoadingBarRef} category="business" />}
/>
<Route
path="/entertainment"
element={<News topLoadingBarRef={this.topLoadingBarRef} category="entertainment" />}
/>
<Route
path="/health"
element={<News topLoadingBarRef={this.topLoadingBarRef} category="health" />}
/>
<Route
path="/science"
element={<News topLoadingBarRef={this.topLoadingBarRef} category="science" />}
/>
<Route
path="/sports"
element={<News topLoadingBarRef={this.topLoadingBarRef} category="sports" />}
/>
<Route
path="/technology"
element={<News topLoadingBarRef={this.topLoadingBarRef} category="technology" />}
/>
</Routes>
</div>
);
}
}
Step 5: Control the Loading Bar from News.js
We pass topLoadingBarRef into our News component as a prop. Inside
fetchArticles, we call continuousStart() when a "fetch" begins and
complete() when it finishes โ this drives the progress bar animation.
fetchArticles = () => {
this.props.topLoadingBarRef.current.continuousStart();
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
this.props.topLoadingBarRef.current.complete();
}, 1000);
};
Step 6: Full Updated News.js
Here is News.js updated to accept a category prop from the route and to drive
the top loading bar alongside the existing spinner and pagination logic.
import React, { Component } from "react";
import NewsItem from "./NewsItem";
import Spinner from "./Spinner";
export class News extends Component {
static defaultProps = {
pageSize: 6,
totalResults: 30,
category: "general",
};
state = {
articles: [
{
title: "React 19 Released",
description: "React 19 brings exciting improvements for developers.",
urlToImage: "",
url: "#",
},
{
title: "GitHub Pages Hosting Made Easy",
description: "Deploy your React app for free using GitHub Pages.",
urlToImage: "",
url: "#",
},
{
title: "JavaScript ES2025 Features",
description: "Explore the newest JavaScript features landing in 2025.",
urlToImage: "",
url: "#",
},
],
page: 1,
loading: false,
pageSize: this.props.pageSize,
};
fetchArticles = () => {
this.props.topLoadingBarRef.current.continuousStart();
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
this.props.topLoadingBarRef.current.complete();
}, 1000);
};
componentDidMount() {
this.fetchArticles();
}
handlePrevClick = () => {
this.setState({ page: this.state.page - 1 }, () => {
this.fetchArticles();
});
};
handleNextClick = () => {
this.setState({ page: this.state.page + 1 }, () => {
this.fetchArticles();
});
};
handlePageSizeChange = (e) => {
const newSize = Number(e.target.value);
this.setState({ pageSize: newSize, page: 1 }, () => {
this.fetchArticles();
});
};
render() {
const { totalResults, category } = this.props;
const { page, pageSize, articles, loading } = this.state;
return (
<div className="container my-4">
<h2 className="text-center mb-4 text-capitalize">
Top {category} Headlines ๐ฐ
</h2>
<div className="d-flex justify-content-end mb-3">
<label className="me-2 align-self-center fw-bold">Articles per page:</label>
<select
className="form-select w-auto"
value={pageSize}
onChange={this.handlePageSizeChange}
>
<option value="3">3</option>
<option value="6">6</option>
<option value="9">9</option>
<option value="12">12</option>
</select>
</div>
{loading ? (
<Spinner />
) : (
articles.map((article, index) => (
<NewsItem
key={index}
title={article.title}
description={article.description}
imageUrl={article.urlToImage}
newsUrl={article.url}
/>
))
)}
<div className="d-flex justify-content-between my-4">
<button
className="btn btn-warning"
onClick={this.handlePrevClick}
disabled={loading || page <= 1}
>
← Previous
</button>
<span className="align-self-center fw-bold">
Page {page} of {Math.ceil(totalResults / pageSize)}
</span>
<button
className="btn btn-warning"
onClick={this.handleNextClick}
disabled={loading || page >= Math.ceil(totalResults / pageSize)}
>
Next →
</button>
</div>
</div>
);
}
}
export default News;
How Routing & the Loading Bar Work Together
| Piece | Role | Triggered By |
|---|---|---|
BrowserRouter |
Enables client-side routing for the whole app | Wraps <App /> in index.js |
Routes / Route |
Maps each category URL to a News component instance |
User navigates via the Navbar links |
NavLink |
Renders navigation links and highlights the active category | Clicked by the user in the Navbar |
topLoadingBarRef |
Lets a child (News) control the parent's LoadingBar imperatively |
continuousStart() and complete() calls inside fetchArticles |
Features and Learnings:-
react-router-dom and react-top-loading-bar.BrowserRouter in index.js.Navbar component with NavLink for category navigation.ref to control a top loading bar from a child component.