Skip to content

Part 7.1 — Routing Fundamentals with React Router

Site Console Site Console
4 min read Updated Feb 3, 2026 Frontend Design 0 comments

Routing is not just navigation.
It’s architecture.

Your routing structure determines:

  • how features are discovered

  • how layouts are shared

  • how state resets (or doesn’t)

  • how URLs communicate intent

In this post, you’ll learn how to use React Router to build a routing system that scales as your app grows—without hacks or accidental complexity.


1. Think of Routes as Features, Not Pages

A common mistake is treating routes like static pages.

Instead, think of routes as feature entry points.

Good routes:

  • reflect user intent

  • group related UI and data

  • map cleanly to backend REST resources

Bad routes are flat, repetitive, and tightly coupled to UI structure.


2. Install and Set Up React Router

Install:

pnpm add react-router-dom

At the app root:

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

function Root() {
  return (
    <BrowserRouter>
      <App />
    </BrowserRouter>
  );
}

React Router now owns navigation state.


3. Define Routes Declaratively

Avoid conditional routing logic scattered across components.

Use a central route definition:

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

function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/users" element={<UsersPage />} />
      <Route path="/users/:id" element={<UserDetail />} />
    </Routes>
  );
}

This makes navigation explicit and discoverable.


4. Layout Routes (The Most Important Concept)

Most real apps share layout across routes.

function AppLayout() {
  return (
    <>
      <Header />
      <main>
        <Outlet />
      </main>
    </>
  );
}

Use it like this:

<Route element={<AppLayout />}>
  <Route path="/" element={<Home />} />
  <Route path="/users" element={<UsersPage />} />
</Route>

Benefits:

  • shared navigation

  • consistent structure

  • no duplicated wrappers

Layout routes are the backbone of scalable routing.


5. Nested Routes = Nested Responsibility

Nested routes mirror feature structure.

<Route path="/users" element={<UsersLayout />}>
  <Route index element={<UsersList />} />
  <Route path=":id" element={<UserDetail />} />
</Route>

This:

  • groups related routes

  • scopes layouts

  • makes ownership clear

Avoid deeply nested routes unless features truly nest.


6. Route Params Are Data, Not Strings

Access params with useParams:

const { id } = useParams<{ id: string }>();

Convert early:

const userId = Number(id);

Never pass raw string params deep into your app.
Type boundaries matter.


7. Index Routes for Default Views

Index routes define what renders at the parent path.

<Route path="/users">
  <Route index element={<UsersList />} />
  <Route path=":id" element={<UserDetail />} />
</Route>

This avoids:

  • duplicate paths

  • conditional rendering hacks

Index routes keep intent clear.


8. Navigation: Declarative, Not Imperative

Prefer:

<Link to="/users">Users</Link>

Over:

navigate('/users');

Imperative navigation is for:

  • form submissions

  • redirects after actions

Declarative navigation is for:

  • menus

  • breadcrumbs

  • links

Use each where it belongs.


9. Programmatic Navigation (Use Sparingly)

const navigate = useNavigate();

navigate(`/users/${id}`);

Common use cases:

  • after create/update/delete

  • after login/logout

  • error recovery flows

Don’t replace links with navigate calls.


10. Error Boundaries per Route

React Router supports route-level error boundaries.

<Route
  path="/users"
  element={<UsersPage />}
  errorElement={<ErrorFallback />}
/>

This prevents:

  • entire app crashes

  • cascading failures

Errors should fail locally, not globally.


11. Route-Driven Data Fetching (Conceptual)

Even without loaders, routes define when data should load.

Rules:

  • fetch data when route becomes active

  • clean up on unmount

  • don’t prefetch everything globally

Routes define lifecycle boundaries.


12. URLs Are Part of Your API

Treat URLs like contracts.

Good URLs:

  • are stable

  • describe resources

  • encode state meaningfully

Bad URLs:

  • reflect UI internals

  • change frequently

  • leak implementation details

If you wouldn’t expose it in a REST API, don’t encode it in a URL.


13. Common Routing Smells

Watch for:

  • deeply nested routes without clear ownership

  • route definitions scattered across files

  • conditional rendering instead of routes

  • global state used to track “current page”

These indicate routing responsibilities are misplaced.


14. Summary

You now know how to:

  • think of routes as architecture

  • define routes declaratively

  • use layout routes effectively

  • structure nested routes cleanly

  • handle params safely

  • separate navigation concerns

  • treat URLs as stable contracts

Related

Leave a comment

Sign in to leave a comment.

Comments