Dev.to WebDev 🛠 Dev 👁 0 📖 5 min read

React Mastery Series – Day 19: Routing in React – Building Single Page Applications with React Router

Welcome back to the React Mastery Series! In the previous article, we explored Custom Hooks in React and learned how reusable logic helps developers build scalable and maintainable applications. Today, we will explore

Welcome back to the React Mastery Series!

In the previous article, we explored Custom Hooks in React and learned how reusable logic helps developers build scalable and maintainable applications.

Today, we will explore one of the most important concepts in modern frontend development:

React Routing

Almost every real-world React application contains multiple screens:

  • Login
  • Dashboard
  • Profile
  • Settings
  • Reports
  • Transactions
  • Admin panels

But React applications are usually built as:

Single Page Applications (SPA)

So how do we navigate between different pages without refreshing the browser?

The answer is React Router

What is Client-Side Routing?

Traditional websites work like this:

User Clicks Link
       |
       ↓
Browser Requests New HTML Page
       |
       ↓
Server Sends Page
       |
       ↓
Browser Reloads

Every navigation causes a full page refresh.

React Single Page Applications work differently:

User Clicks Link
       |
       ↓
React Router Intercepts Request
       |
       ↓
URL Changes
       |
       ↓
React Loads Component
       |
       ↓
No Page Refresh

This creates a smooth application experience.

What is React Router?

React Router is a library that enables navigation between different components based on the URL.

Example:

/login
/dashboard
/profile
/settings

Each URL maps to a React component.

Example:

/login
   |
   ↓
Login Component


/dashboard
   |
   ↓
Dashboard Component

Installing React Router

For a React application:

npm install react-router-dom

The package provides:

  • BrowserRouter
  • Routes
  • Route
  • Link
  • Navigate
  • useNavigate
  • useParams

Setting Up BrowserRouter

The first step is wrapping your application.

Example:

import { BrowserRouter } from "react-router-dom";

import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
);

Now React can manage browser navigation.

Creating Routes

Routes define which component should display for a URL.

Example:

import { Routes, Route } from "react-router-dom";

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />

      <Route path="/login" element={<Login />} />

      <Route path="/dashboard" element={<Dashboard />} />
    </Routes>
  );
}

Now:

/login

renders:

<Login />

Understanding Route Components

Example:

<Route path="/profile" element={<Profile />} />;

Contains path and element

The URL path specifies the url appeared in browser

Example:

/profile

element

The component to render on visiting the path specified.

Example:

<Profile />

Navigation Using Link

Traditional HTML - causes a browser refresh.

<a href="/dashboard"> Dashboard </a>

React Router provides:

import { Link } from "react-router-dom";
<Link to="/dashboard">Dashboard</Link>

Benefits:

  • No page refresh
  • SPA behavior
  • Better performance

Creating a Navigation Bar

Example:

function Navbar() {
  return (
    <nav>
      <Link to="/">Home</Link>

      <Link to="/dashboard">Dashboard</Link>

      <Link to="/profile">Profile</Link>
    </nav>
  );
}

When users click:

Dashboard

React loads:

<Dashboard />

without refreshing.

Programmatic Navigation with useNavigate

Sometimes navigation happens after an action.

Examples:

  • Successful login
  • Logout
  • Form submission

Example:

import { useNavigate } from "react-router-dom";

function Login() {
  const navigate = useNavigate();

  function handleLogin() {
    // API success

    navigate("/dashboard");
  }

  return <button onClick={handleLogin}>Login</button>;
}

Flow:

User Login
     |
     ↓
API Success
     |
     ↓
navigate("/dashboard")
     |
     ↓
Dashboard Page

Route Parameters

Many applications need dynamic URLs.

Example:

/users/101

Here:

101

is dynamic.

React Router supports this using parameters.

Example:

<Route path="/users/:id" element={<UserDetails />} />

Reading Route Parameters

Using:

useParams()

Example:

import { useParams } from "react-router-dom";

function UserDetails() {
  const { id } = useParams();

  return <h2>User ID: {id}</h2>;
}

URL:

/users/101

Output:

User ID: 101

Real-World Example: Banking Application

Consider:

/accounts/12345

where:

12345

is the account number.

Route:

<Route path="/accounts/:accountId" element={<AccountDetails />} />

Component:

const { accountId }=useParams();

Now the application knows which account to display.

Nested Routes

Large applications often have sections inside sections.

Example:

Dashboard

   |
   ├── Overview

   ├── Transactions

   └── Profile

Routes:

<Route>
  <Route path="/dashboard" element={<Dashboard />} >
  <Route path="transactions" element={<Transactions />} />
  <Route path="profile" element={<Profile />} />
</Routes>

URL:

/dashboard/transactions

renders:

Dashboard
    +
Transactions

Outlet Component

Nested routes use:

<Outlet />

Example:

import { Outlet } from "react-router-dom";

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>

      <Outlet />
    </div>
  );
}

The child route appears where Outlet exists.

Protected Routes

Enterprise applications often require authentication.

Example:

Users should access:

/dashboard

only after login.

Without authentication:

/dashboard
       |
       ↓
Redirect
       |
       ↓
/login

Creating ProtectedRoute

Example:

function ProtectedRoute({ children }) {
  const isAuthenticated = true;

  if (!isAuthenticated) {
    return <Navigate to="/login" />;
  }

  return children;
}

Usage:

<Route
  path="/dashboard"

  element={
    <ProtectedRoute>
      <Dashboard />
    </ProtectedRoute>
  }
/>;

Real-World Authentication Flow

Enterprise applications usually follow:

User Opens Application

        |
        ↓

Check Token

        |
        ↓

Token Exists?

        |
   -------------
   |           |
 Yes           No
   |            |
Dashboard     Login

React Router handles navigation decisions.

Lazy Loading Routes

Large applications should not load every page immediately.

Example:

const Dashboard = lazy(() => import("./Dashboard") );

Route:

<Suspense fallback="Loading...">
  <Routes>
    <Route path="/dashboard" element={<Dashboard />} />
  </Routes>
</Suspense>;

Benefits:

  • Smaller initial bundle
  • Faster startup
  • Better performance

Route Organization in Enterprise Projects

A scalable structure:

src

├── routes
│    ├── AppRoutes.jsx
│    ├── ProtectedRoute.jsx
├── pages
│    ├── Login.jsx
│    ├── Dashboard.jsx
│    └── Profile.jsx
├── layouts
│    └── MainLayout.jsx

Real-World Banking Application Routing

Example:

/login

/dashboard

/accounts

/accounts/:id

/transactions

/profile

/settings

Flow:

Login
 |
 ↓
Authentication Success
 |
 ↓
Dashboard
 |
 ├── Accounts
 |
 ├── Transactions
 |
 └── Profile

This is the architecture used in many enterprise React applications.

Common Mistakes

1. Using Anchor Tags

Avoid:

<a href="/dashboard"> Dashboard </a>

Use:

<Link to="/dashboard"> Dashboard </Link>

2. Forgetting BrowserRouter

Without:

<BrowserRouter>

routing will not work.

3. Incorrect Nested Routes

Parent route must contain:

<Outlet />

otherwise child routes won't appear.

4. Loading All Pages at Startup

Large applications should use:

  • lazy loading
  • code splitting

Best Practices

  • Keep routes centralized.
  • Use lazy loading for large applications.
  • Create reusable protected routes.
  • Use meaningful URL structures.
  • Avoid unnecessary route nesting.
  • Separate page components from reusable components.

Key Takeaways

Today, we learned:

✅ React Router enables navigation in Single Page Applications.
✅ Routes map URLs to React components.
✅ Link provides SPA navigation without refresh.
✅ useNavigate enables programmatic navigation.
✅ useParams reads dynamic route values.
✅ Protected routes secure application pages.
✅ Lazy loading improves application performance.

Coming Next 🚀

In Day 20, we will explore:

Building Production-Ready React Applications – Project Structure and Architecture

We will learn:

  • Scalable folder structure
  • Component organization
  • Feature-based architecture
  • Separation of concerns
  • API service layers
  • Error handling
  • Environment configuration
  • Enterprise React project patterns

This will connect all the concepts we learned so far into a real-world production architecture.

Happy Coding! 🚀

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.