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:
What React Router DOM is and why single page apps need it
Installing react-router-dom and react-top-loading-bar
Setting up BrowserRouter, Routes, and Route in App.js
Creating category-based routes for the News App
Building a Navbar component with NavLink
Controlling a top loading bar using a ref from a child component
Triggering the loading bar during pagination and category changes
---
What 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:-
Understood what React Router DOM is and why it's essential for multi-page SPAs.
Installed and configured react-router-dom and react-top-loading-bar.
Wrapped the app with BrowserRouter in index.js.
Built a Navbar component with NavLink for category navigation.
Created dedicated routes for Business, Entertainment, General, Health, Science, Sports, and Technology.
Used a ref to control a top loading bar from a child component.
Triggered the loading bar during pagination, page size changes, and category switches.
Prepared the app for the next step: fetching real articles from a News API per category.