← Back to Articles         
React Router

React Router

Published on 12 Nov 2026 32 min read React
React Fundamentals

1. What is React Router?

React Router is a library used to handle navigation and URLs in React applications.

For example, suppose you have a React application:

Home
Products
About
Contact

You may want URLs like:

/
 /products
 /about
 /contact

React Router connects these URLs to React components.

For example:

URL                         React Component

/                           Home
/products                   Products
/products/101               ProductDetails
/about                      About
/contact                    Contact

So when the user visits:

/products

React Router can render:

<Products />

instead of the Home component.

The important point is:

React Router allows a React application to behave like a multi-page application while still being a Single Page Application (SPA).

The browser URL changes, but React normally does not reload the entire HTML page.


2. Why Do We Need React Router?

Without React Router, you could manually check window.location.pathname.

For example:

const path = window.location.pathname;

if (path === "/") {
  return <Home />;
}

if (path === "/products") {
  return <Products />;
}

if (path === "/about") {
  return <About />;
}

This works for a very small application, but becomes difficult to maintain.

Imagine an application with:

/
/products
/products/1
/products/2
/products/3
/cart
/checkout
/login
/register
/account
/account/profile
/account/orders
/admin
/admin/users
/admin/products

You don't want to manually manage all these routes.

React Router provides a structured routing system.

It gives you features such as:

  • Route configuration
  • Navigation
  • Dynamic routes
  • Nested routes
  • Route parameters
  • Query parameters
  • Redirect/navigation
  • Protected routes
  • Layout routes
  • Error routes
  • 404 pages
  • Lazy loading
  • Data loading
  • Form actions
  • Navigation state
  • Route-based code splitting

3. React Router and Single Page Applications

React applications are commonly built as Single Page Applications (SPAs).

Consider:

Browser
   |
   | https://example.com/products
   |
React Application
   |
   +-- React Router
          |
          +-- /products
                 |
                 +-- Products component

When the user navigates from:

/products

to:

/about

React Router changes the URL and determines which React component should be displayed.

The browser does not normally download a completely new HTML document.

The flow is approximately:

User clicks link
       |
       v
React Router intercepts navigation
       |
       v
URL changes
       |
       v
Router matches URL
       |
       v
Matching React component renders
       |
       v
React updates the DOM

This is one of the main reasons React Router is useful.


4. Installing React Router

For modern React applications, install:

npm install react-router-dom

Then you can import routing functionality from:

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

5. Basic React Router Example

Suppose you have:

src/
  App.tsx
  Home.tsx
  About.tsx
  Contact.tsx

Home.tsx

export default function Home() {
  return <h1>Home Page</h1>;
}

About.tsx

export default function About() {
  return <h1>About Page</h1>;
}

Contact.tsx

export default function Contact() {
  return <h1>Contact Page</h1>;
}

Now configure the routes.

App.tsx

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

import Home from "./Home";
import About from "./About";
import Contact from "./Contact";

function App() {
  return (
    <BrowserRouter>

      <Routes>

        <Route path="/" element={<Home />} />

        <Route path="/about" element={<About />} />

        <Route path="/contact" element={<Contact />} />

      </Routes>

    </BrowserRouter>
  );
}

export default App;

Now:

http://localhost:5173/

renders:

Home Page

And:

http://localhost:5173/about

renders:

About Page

And:

http://localhost:5173/contact

renders:

Contact Page

6. BrowserRouter

BrowserRouter provides the routing context to your React application.

Example:

<BrowserRouter>
  <App />
</BrowserRouter>

or, if routing is configured inside App:

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

React Router needs this context so that components can access routing functionality.

Without it, components using things such as:

<Link />
useNavigate()
useLocation()
useParams()

cannot work correctly.

A common architecture is:

main.tsx
   |
   v
BrowserRouter
   |
   v
App
   |
   +-- Routes
         |
         +-- Home
         +-- Products
         +-- About

7. Routes and Route

Two important components are:

<Routes>

and:

<Route />

Example:

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/products" element={<Products />} />
</Routes>

Think of it as:

URL                Component

/                  Home
/about             About
/products          Products

Routes manages the collection of routes.

Route defines an individual route.


8. Navigation Using Link

You should generally use React Router's Link instead of a normal HTML <a> element for internal application navigation.

Example:

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

function Navbar() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/products">Products</Link>
    </nav>
  );
}

The user can click:

Home | About | Products

and React Router handles the navigation.

Why not <a href>?

You can write:

<a href="/products">Products</a>

but this can cause a normal browser navigation.

For internal SPA navigation, prefer:

<Link to="/products">
  Products
</Link>

9. Link vs NavLink

Link is used for navigation.

NavLink is useful when you want to know whether the current route is active.

For example:

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

<NavLink to="/products">
  Products
</NavLink>

You can dynamically apply CSS:

<NavLink
  to="/products"
  className={({ isActive }) =>
    isActive ? "active" : ""
  }
>
  Products
</NavLink>

If the current URL is:

/products

then:

isActive === true

This is particularly useful for:

  • Navigation menus
  • Sidebars
  • Admin dashboards
  • Tabs
  • Breadcrumb navigation

10. Dynamic Routes

One of the most important features of React Router is the dynamic route.

Suppose you have products:

Product 101
Product 102
Product 103

You might want URLs:

/products/101
/products/102
/products/103

You don't want to create three separate routes.

Instead:

<Route
  path="/products/:id"
  element={<ProductDetails />}
/>

Here:

:id

is a route parameter.


11. Reading Route Parameters with useParams

Inside the component:

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

function ProductDetails() {

  const { id } = useParams();

  return (
    <h1>
      Product ID: {id}
    </h1>
  );
}

If the URL is:

/products/101

then:

id

contains:

101

If the URL is:

/products/500

then:

id

contains:

500

The flow is:

/products/101
       |
       v
React Router matches
       |
       v
/products/:id
       |
       v
id = "101"
       |
       v
ProductDetails

12. Real-World Dynamic Route Example

Suppose an API returns:

[
  {
    "id": 101,
    "name": "Laptop"
  },
  {
    "id": 102,
    "name": "Keyboard"
  }
]

Your product listing might contain:

<Link to={`/products/${product.id}`}>
  {product.name}
</Link>

For Laptop:

/products/101

For Keyboard:

/products/102

Then:

function ProductDetails() {

  const { id } = useParams();

  // Fetch product using id

}

This is one of the most common real-world uses of React Router.


13. Route Parameters vs Query Parameters

This is an important interview topic.

Route parameter

/products/101

Route:

<Route
  path="/products/:id"
  element={<ProductDetails />}
/>

Used when the value identifies a specific resource.

For example:

/products/101
/users/50
/orders/9001

Query parameter

/products?category=laptop

Query parameters are usually used for filtering, searching, sorting, pagination, etc.

For example:

/products?category=laptop&page=2

Conceptually:

Path parameter
/products/101
          |
          +-- identifies product

Query parameter
/products?category=laptop
                    |
                    +-- filters products

14. Query Parameters with useSearchParams

React Router provides:

useSearchParams()

Example:

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

function Products() {

  const [searchParams, setSearchParams] =
    useSearchParams();

  const category =
    searchParams.get("category");

  return (
    <div>
      Category: {category}
    </div>
  );
}

For:

/products?category=laptop

the result is:

Category: laptop

15. Updating Query Parameters

You can change query parameters:

setSearchParams({
  category: "laptop"
});

The URL becomes:

/products?category=laptop

You can also have:

setSearchParams({
  category: "laptop",
  page: "2"
});

Result:

/products?category=laptop&page=2

This is very useful for search pages.


16. Real-World Search Scenario

Imagine an e-commerce application.

User searches:

laptop

You could represent it as:

/products?search=laptop

User selects:

Electronics

and page 2:

/products?search=laptop&category=electronics&page=2

Now the URL itself represents the current search state.

This has a major advantage:

The user can copy the URL and share it with another person.

The other person can open the same URL and see the same search/filter state.


17. Programmatic Navigation with useNavigate

Sometimes navigation needs to happen from JavaScript rather than from a link.

React Router provides:

useNavigate()

Example:

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

function Login() {

  const navigate = useNavigate();

  function handleLogin() {

    // login successful

    navigate("/dashboard");
  }

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

The flow is:

User clicks Login
       |
       v
Authentication succeeds
       |
       v
navigate("/dashboard")
       |
       v
URL changes
       |
       v
Dashboard renders

18. navigate with Browser History

You can navigate backward:

navigate(-1);

Forward:

navigate(1);

For example:

<button onClick={() => navigate(-1)}>
  Back
</button>

This is useful when implementing custom navigation buttons.


19. Replace Navigation

Normally:

navigate("/dashboard");

adds a new browser history entry.

You can replace the current entry:

navigate("/dashboard", {
  replace: true
});

This is useful for scenarios such as login redirects.

For example:

/login
   |
   | successful login
   v
/dashboard

You may not want the user to press Back and return to /login.

Therefore:

navigate("/dashboard", {
  replace: true
});

can be appropriate.


20. Nested Routes

Nested routing is extremely useful in real applications.

Consider an admin application:

/admin
/admin/users
/admin/products
/admin/settings

You could configure:

<Route path="/admin" element={<AdminLayout />}>
  <Route path="users" element={<Users />} />
  <Route path="products" element={<Products />} />
  <Route path="settings" element={<Settings />} />
</Route>

This means:

/admin
    |
    +-- AdminLayout
          |
          +-- users
          +-- products
          +-- settings

21. Outlet

Nested routes normally use:

<Outlet />

inside the parent layout.

Example:

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

function AdminLayout() {
  return (
    <div>

      <h1>Admin Panel</h1>

      <nav>
        ...
      </nav>

      <main>
        <Outlet />
      </main>

    </div>
  );
}

Now:

/admin/users

renders:

Admin Panel
Navigation

Users

And:

/admin/products

renders:

Admin Panel
Navigation

Products

The AdminLayout remains while the child route changes.


22. Why Outlet Is Important

Think of <Outlet /> as a placeholder for child routes.

For example:

<AdminLayout>

   Header

   Sidebar

   <Outlet />

   Footer

</AdminLayout>

If the current route is:

/admin/users

then <Outlet /> becomes:

<Users />

If the route is:

/admin/products

then <Outlet /> becomes:

<Products />

This is one of the most important concepts in React Router.


23. Layout Routes

A layout route lets multiple pages share the same UI.

For example:

Website
 ├── Header
 ├── Navigation
 ├── Main Content
 └── Footer

You don't want to repeat the Header and Footer in every page.

Instead:

<Route element={<PublicLayout />}>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/contact" element={<Contact />} />
</Route>

And:

function PublicLayout() {
  return (
    <>
      <Header />

      <main>
        <Outlet />
      </main>

      <Footer />
    </>
  );
}

This creates a clean application structure.


24. Index Routes

Suppose you have:

/admin
/admin/users
/admin/products

You may want /admin to display a default page.

You can use:

<Route path="/admin" element={<AdminLayout />}>

  <Route
    index
    element={<Dashboard />}
  />

  <Route
    path="users"
    element={<Users />}
  />

  <Route
    path="products"
    element={<Products />}
  />

</Route>

The index route represents the default child route.

Therefore:

/admin

renders:

Dashboard

25. 404 Not Found Route

A professional application should handle invalid URLs.

Example:

<Route
  path="*"
  element={<NotFound />}
/>

The * catches URLs that don't match your other routes.

For example:

/abcxyz

might display:

404
Page Not Found

A common pattern:

<Routes>

  <Route path="/" element={<Home />} />

  <Route path="/about" element={<About />} />

  <Route path="/products" element={<Products />} />

  <Route
    path="*"
    element={<NotFound />}
  />

</Routes>

26. Protected Routes

This is extremely common in real applications.

Suppose your application has:

/public
/login
/dashboard
/profile
/admin

Anyone can access:

/login

but only authenticated users should access:

/dashboard
/profile

A protected route checks authentication before rendering the protected page.

Conceptually:

User requests /dashboard
          |
          v
Is user authenticated?
       /       \
     Yes        No
      |          |
      v          v
 Dashboard      Login

27. Simple Protected Route Example

Suppose:

function ProtectedRoute() {

  const isAuthenticated =
    localStorage.getItem("token");

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

  return <Outlet />;
}

Then configure:

<Route element={<ProtectedRoute />}>

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

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

</Route>

Now:

/dashboard
/profile

are protected.


28. Navigate vs Navigate Component

There are two related concepts.

useNavigate

Used inside JavaScript logic:

const navigate = useNavigate();

navigate("/dashboard");

<Navigate />

Used when rendering needs to redirect:

return <Navigate to="/login" replace />;

A useful way to remember:

useNavigate()
    |
    +-- imperative/programmatic navigation

<Navigate />
    |
    +-- navigation during rendering

29. Role-Based Routing

Authentication alone may not be enough.

Suppose you have:

Admin
Manager
Employee

And:

/admin
/manager
/employee

You may want to check the user's role.

For example:

function AdminRoute() {

  const role = localStorage.getItem("role");

  if (role !== "admin") {
    return <Navigate to="/unauthorized" replace />;
  }

  return <Outlet />;
}

Then:

<Route element={<AdminRoute />}>

  <Route
    path="/admin"
    element={<AdminDashboard />}
  />

</Route>

This creates role-based route protection.


30. Authentication vs Authorization

These concepts are often confused.

Authentication

Answers:

Who are you?

Example:

User has valid JWT token

Authorization

Answers:

What are you allowed to access?

Example:

User is authenticated
but does not have Admin role

Therefore:

Authentication
      |
      v
Is the user logged in?
      |
      v
Authorization
      |
      v
Does the user have permission?

React Router can help enforce frontend navigation rules, but real authorization must also be enforced by the backend/API.

Never rely only on React Router for security.


31. Child Route Parameters

You can combine nested routes and parameters.

Example:

<Route path="/users" element={<UsersLayout />}>

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

</Route>

URL:

/users/101

Then:

const { userId } = useParams();

returns:

101

32. Multiple Route Parameters

You can have:

<Route
  path="/users/:userId/orders/:orderId"
  element={<OrderDetails />}
/>

URL:

/users/10/orders/500

Then:

const {
  userId,
  orderId
} = useParams();

Result:

userId = "10"
orderId = "500"

This is useful for hierarchical resources.


33. Optional Parameters and Modern Routing

Routing syntax can change between React Router versions, so avoid copying older tutorials blindly.

For current React Router projects, check the version documentation when using advanced route-pattern features.

For common applications, prefer clear routes such as:

/products
/products/:id

and query parameters such as:

/products?category=electronics

rather than making route patterns unnecessarily complex.


34. Relative Navigation

Nested routing supports relative paths.

Suppose the current route is:

/products/101

You can use:

<Link to="reviews">
  Reviews
</Link>

which can resolve to:

/products/101/reviews

This is particularly useful with nested routes.


35. Absolute vs Relative Routes

Absolute

<Link to="/products">
  Products
</Link>

Starts from the application root.

Relative

<Link to="reviews">
  Reviews
</Link>

Resolves relative to the current route context.

Understanding this becomes important when working with nested routes.


36. Navigation State

Sometimes you want to send temporary data during navigation.

For example:

navigate("/product", {
  state: {
    productName: "Laptop"
  }
});

The destination can read the state using:

const location = useLocation();

console.log(location.state);

This can be useful for UI navigation state.

However, don't treat navigation state as a replacement for persistent application data or URL parameters.


37. useLocation

useLocation() gives information about the current location.

Example:

const location = useLocation();

console.log(location.pathname);
console.log(location.search);
console.log(location.hash);

For:

/products?category=laptop#reviews

you can conceptually access:

pathname = /products
search   = ?category=laptop
hash     = #reviews

This is useful when your component needs to react to URL changes.


38. Example: Detecting URL Changes

const location = useLocation();

useEffect(() => {
  console.log("URL changed:", location.pathname);
}, [location.pathname]);

This can be useful for:

  • Analytics
  • Page tracking
  • UI behavior
  • Route-specific logic

Be careful not to put unnecessary logic into global location listeners.


39. Route-Based Data Fetching

A very common real-world pattern is:

/products/101
       |
       v
useParams()
       |
       v
id = 101
       |
       v
API request
       |
       v
GET /api/products/101
       |
       v
Product data
       |
       v
Render ProductDetails

Example:

function ProductDetails() {

  const { id } = useParams();

  useEffect(() => {

    fetch(`/api/products/${id}`)
      .then(response => response.json())
      .then(data => {
        // update state
      });

  }, [id]);

  return <div>Product Details</div>;
}

The important idea is that the route parameter drives the data request.


40. React Router with useState

React Router and useState often work together.

For example:

function Products() {

  const [products, setProducts] = useState<Product[]>([]);

  // fetch products

  return (
    ...
  );
}

The route determines which page is displayed, while state manages data and UI state inside that page.

Think:

React Router
     |
     +-- Which page?
     |
     v
Products component
     |
     +-- useState
     |
     +-- products
     +-- loading
     +-- selected filter

41. React Router with useEffect

A route parameter often becomes a dependency for useEffect.

const { id } = useParams();

useEffect(() => {

  loadProduct(id);

}, [id]);

If the user changes:

/products/101

to:

/products/102

the component can react to the changed parameter and fetch the new product.


42. React Router with Context API

Suppose you have authentication information in Context:

<AuthContext>

The router can use authentication information from that context.

Conceptually:

AuthContext
     |
     +-- user
     +-- token
     +-- role
     |
     v
ProtectedRoute
     |
     v
React Router
     |
     v
Protected Pages

Example:

const { user } = useAuth();

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

This is cleaner than repeatedly checking localStorage throughout the application.


43. React Router with Redux

React Router can also work with Redux.

For example:

Router
  |
  +-- Product Page
          |
          +-- Redux store
                |
                +-- products
                +-- cart
                +-- user

The URL may contain:

/products/101

Redux may contain:

cart
user
products

They solve different problems.

Router

Manages:

Navigation + URL

Redux

Manages:

Global application state

Do not use Redux simply because you need routing.


44. React Router with Forms

Modern React Router also supports route-oriented data APIs.

Depending on the router architecture you choose, routes can define:

  • loaders
  • actions
  • forms
  • error boundaries

This can move data loading and mutations closer to the route definition.

For example, a route can conceptually be:

{
  path: "/products",
  loader: loadProducts,
  element: <Products />
}

Then the route can load its data before rendering the page.


45. Loader Concept

A loader is used to load route data.

Conceptually:

User navigates to /products
           |
           v
Router executes loader
           |
           v
API request
           |
           v
Product data
           |
           v
Products component

This is different from manually doing everything inside:

useEffect()

It allows routing and data loading to be integrated.


46. Example of createBrowserRouter

React Router provides a data-router approach using:

createBrowserRouter()

Example:

import {
  createBrowserRouter,
  RouterProvider
} from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Home />
  },
  {
    path: "/products",
    element: <Products />
  }
]);

function App() {
  return (
    <RouterProvider router={router} />
  );
}

This is another important way to configure React Router.


47. JSX Routes vs Data Router Configuration

There are two common styles.

JSX route configuration

<BrowserRouter>
  <Routes>

    <Route
      path="/"
      element={<Home />}
    />

    <Route
      path="/products"
      element={<Products />}
    />

  </Routes>
</BrowserRouter>

Data router

const router = createBrowserRouter([
  {
    path: "/",
    element: <Home />
  },
  {
    path: "/products",
    element: <Products />
  }
]);

Then:

<RouterProvider router={router} />

Both are valid approaches.

The data-router architecture becomes especially useful when you need route-level:

  • loaders
  • actions
  • error handling
  • data APIs
  • navigation state

48. Lazy Loading Routes

Large applications should not necessarily download every page's JavaScript immediately.

You can lazy-load routes.

React provides:

lazy()

and:

Suspense

Example:

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

Then:

<Suspense fallback={<p>Loading...</p>}>
  <Dashboard />
</Suspense>

The idea is:

Application starts
       |
       v
Load required code
       |
       v
User opens Dashboard
       |
       v
Download Dashboard chunk
       |
       v
Render Dashboard

This can improve initial load performance in large applications.


49. Route-Based Code Splitting

Imagine your application contains:

Home
Products
Reports
Admin
Analytics
Settings

Without code splitting, the browser may download a large JavaScript bundle.

With route-based lazy loading:

Initial load
   |
   +-- Home code
   +-- Common code

Later:
   |
   +-- Products chunk
   +-- Reports chunk
   +-- Admin chunk

This is especially useful for large enterprise applications.


50. Error Handling

Modern React Router supports route-level error handling.

Conceptually:

{
  path: "/products",
  element: <Products />,
  errorElement: <ErrorPage />
}

If something goes wrong in route handling/data loading, an appropriate error UI can be displayed.

This is better than allowing the whole application to become confusing when a route-specific operation fails.


51. Pending Navigation

Modern data-router APIs also expose navigation state.

Conceptually:

const navigation = useNavigation();

You can determine whether navigation is occurring.

For example:

if (navigation.state === "loading") {
  return <p>Loading...</p>;
}

This can be used for:

  • Loading indicators
  • Progress indicators
  • Disabling controls
  • Navigation feedback

52. React Router in a Real E-Commerce Application

Imagine an e-commerce application:

/
├── products
│   ├── :productId
│   └── :productId/reviews
├── cart
├── checkout
├── login
└── account
    ├── profile
    └── orders
        └── :orderId

A possible route structure:

<Routes>

  <Route path="/" element={<Home />} />

  <Route path="/products" element={<Products />} />

  <Route
    path="/products/:productId"
    element={<ProductDetails />}
  />

  <Route
    path="/products/:productId/reviews"
    element={<Reviews />}
  />

  <Route path="/cart" element={<Cart />} />

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

  <Route element={<ProtectedRoute />}>

    <Route
      path="/account"
      element={<AccountLayout />}
    >

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

      <Route
        path="orders"
        element={<Orders />}
      />

      <Route
        path="orders/:orderId"
        element={<OrderDetails />}
      />

    </Route>

  </Route>

</Routes>

This demonstrates several important concepts together:

  • Static routes
  • Dynamic routes
  • Nested routes
  • Layouts
  • Protected routes
  • Route parameters

53. Real-World Admin Dashboard

An admin application might look like:

/admin
    |
    +-- Dashboard
    |
    +-- Users
    |     |
    |     +-- User Details
    |
    +-- Products
    |
    +-- Orders
    |
    +-- Reports
    |
    +-- Settings

A route configuration could be:

<Route
  path="/admin"
  element={<AdminLayout />}
>
  <Route
    index
    element={<Dashboard />}
  />

  <Route
    path="users"
    element={<Users />}
  />

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

  <Route
    path="products"
    element={<Products />}
  />

  <Route
    path="orders"
    element={<Orders />}
  />

  <Route
    path="reports"
    element={<Reports />}
  />

  <Route
    path="settings"
    element={<Settings />}
  />
</Route>

This is a very common enterprise pattern.


54. Routing Architecture for a Large Application

A larger project might use:

src/
│
├── components/
│   ├── Header.tsx
│   └── Sidebar.tsx
│
├── layouts/
│   ├── PublicLayout.tsx
│   └── AdminLayout.tsx
│
├── pages/
│   ├── Home.tsx
│   ├── Login.tsx
│   ├── Products.tsx
│   └── ProductDetails.tsx
│
├── routes/
│   └── AppRoutes.tsx
│
├── services/
│   └── productService.ts
│
├── context/
│   └── AuthContext.tsx
│
└── App.tsx

Then routing can be centralized.

For example:

function AppRoutes() {
  return (
    <Routes>

      ...

    </Routes>
  );
}

And:

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

This becomes easier to maintain than putting every route directly into a huge App.tsx.


55. TypeScript with useParams

This is an important point.

useParams() values are strings.

Example:

const { id } = useParams();

The value should be treated as potentially undefined.

You can provide a type:

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

Then:

if (!id) {
  return <p>Invalid product</p>;
}

Remember that URL values are strings.

Even if your URL contains:

/products/100

you receive:

id === "100"

not:

id === 100

If an API expects a number:

const productId = Number(id);

56. TypeScript with Navigation State

Suppose you navigate with state:

navigate("/product", {
  state: {
    productId: 101,
    name: "Laptop"
  }
});

You can read:

const location = useLocation();

But TypeScript may require you to define the expected state shape yourself.

A safer pattern is to define:

type ProductNavigationState = {
  productId: number;
  name: string;
};

Then use that type when reading the state.

The important idea is:

Navigation state is runtime data, so TypeScript does not magically validate that the other page actually received the expected object.


57. TypeScript Route Configuration

You can create route definitions using TypeScript:

const routes = [
  {
    path: "/",
    element: <Home />
  },
  {
    path: "/products",
    element: <Products />
  },
  {
    path: "/products/:id",
    element: <ProductDetails />
  }
];

The React Router types provide strong typing around routing APIs.

For most applications, you don't need to create complicated custom route types.


58. Authentication Flow with React Router

A common application flow is:

Application starts
       |
       v
Check authentication
       |
       +---- Not logged in
       |          |
       |          v
       |       Login Page
       |
       +---- Logged in
                  |
                  v
             Dashboard

After login:

Login
  |
  v
API authentication
  |
  v
JWT/token received
  |
  v
Authentication state updated
  |
  v
navigate("/dashboard")

When accessing a protected page:

/dashboard
     |
     v
ProtectedRoute
     |
     v
Is user authenticated?
     |
   Yes ----> Dashboard
     |
    No
     |
     v
Login

59. React Router and JWT

React Router itself does not authenticate users.

Your backend may issue a JWT:

Login API
    |
    v
JWT
    |
    v
AuthContext
    |
    v
ProtectedRoute
    |
    v
React Router

React Router decides which UI route should be shown.

The API must still validate the JWT on protected API calls.

Therefore:

Frontend route protection improves user experience and navigation control, but backend authorization provides the actual security boundary.


60. React Router and API Calls

React Router doesn't replace Axios or fetch.

They have different responsibilities.

Technology Responsibility
React Router Navigation and URL
Axios/fetch HTTP requests
useState Local component state
Context Shared application state
Redux Global state management
React Router loaders Route-level data loading

For example:

/products/101
      |
      v
React Router
      |
      v
ProductDetails
      |
      v
useParams()
      |
      v
id = 101
      |
      v
Axios
      |
      v
GET /api/products/101

61. When Should You Use React Router?

React Router is appropriate when your application has multiple navigable views.

Examples:

  • E-commerce
  • Admin dashboard
  • Portfolio
  • Learning platform
  • SaaS application
  • CRM
  • ERP
  • Blog
  • Banking UI
  • Social application

If your application has:

Home
Products
Product Details
Login
Dashboard
Profile
Settings

React Router is usually appropriate.


62. When You May Not Need React Router

You don't necessarily need React Router for a very small application.

For example:

Single component
Single screen
No URL-based navigation
No multiple pages

You could simply use:

function App() {
  return <Counter />;
}

Adding a routing library to a tiny one-screen application may provide little value.


63. React Router vs Conditional Rendering

You could write:

const [page, setPage] = useState("home");

if (page === "home") {
  return <Home />;
}

if (page === "about") {
  return <About />;
}

This can work for simple UI switching.

But it doesn't naturally provide:

URL
Browser history
Deep linking
Back/forward navigation
Dynamic routes
Nested routes
Shareable URLs

React Router is designed for these requirements.


64. Common Mistake: Using <a> for Internal Navigation

Instead of:

<a href="/products">
  Products
</a>

prefer:

<Link to="/products">
  Products
</Link>

for internal SPA routes.

Use regular <a> when navigating to an external website, for example:

<a
  href="https://example.com"
  target="_blank"
  rel="noreferrer"
>
  External Website
</a>

65. Common Mistake: Forgetting BrowserRouter

If you use:

<Link to="/products">
  Products
</Link>

but your application isn't inside a router, you can get routing-context errors.

Make sure the application is wrapped appropriately:

<BrowserRouter>
  <App />
</BrowserRouter>

66. Common Mistake: Incorrect Nested Paths

Suppose:

<Route path="/admin" element={<AdminLayout />}>
  <Route path="/users" element={<Users />} />
</Route>

The child route beginning with / is an absolute path.

For normal nested routing, use:

<Route path="users" element={<Users />} />

Then the final URL becomes:

/admin/users

This distinction is important.


67. Common Mistake: Forgetting Outlet

You define:

<Route path="/admin" element={<AdminLayout />}>
  <Route path="users" element={<Users />} />
</Route>

but your layout does not contain:

<Outlet />

Then the child component has nowhere to render.

You generally need:

function AdminLayout() {
  return (
    <>
      <Sidebar />

      <Outlet />
    </>
  );
}

68. Common Mistake: Treating URL Parameters as Numbers

This:

/products/100

produces:

id === "100"

not:

id === 100

Therefore:

const numericId = Number(id);

may be required before sending it to an API that expects a numeric identifier.


69. Common Mistake: Relying on Route Protection for Security

This is dangerous:

if (user.role !== "admin") {
  return <Navigate to="/login" />;
}

This only controls the frontend.

A user can still call your API directly.

Your backend should also check:

JWT
+
Claims
+
Roles/Permissions

For example:

React Router
     |
     | UI protection
     v
Admin Page
     |
     v
API
     |
     | Backend authorization
     v
Database

Both frontend and backend should enforce appropriate controls.


70. Common Mistake: Putting Everything in App.tsx

This:

function App() {

  // 100 routes
  // authentication
  // layouts
  // navigation
  // business logic

}

becomes difficult to maintain.

For larger applications, separate:

routes/
layouts/
pages/
components/
services/

according to your application's size and architecture.


71. Best Practice: Keep URLs Meaningful

Prefer:

/products
/products/101
/orders/500
/users/25

instead of:

/page1
/page2
/x123

Good URLs make applications easier to understand and debug.


72. Best Practice: Use Query Parameters for UI Filters

Instead of keeping all search/filter state only in React state:

const [category, setCategory] =
  useState("laptop");

consider URL state when it should be shareable:

/products?category=laptop

This is especially useful for:

  • Search
  • Filters
  • Pagination
  • Sorting
  • Tabs

73. Best Practice: Use Layout Routes

If multiple pages share:

Header
Sidebar
Footer

use a layout route.

Instead of duplicating:

<Header />
<Sidebar />
<Footer />

in every page, use:

<Layout>
  <Outlet />
</Layout>

This improves maintainability.


74. Best Practice: Use Lazy Loading for Large Applications

For large applications:

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

can reduce initial JavaScript loading.

Use route-based code splitting especially when some pages are large or rarely visited.


75. Best Practice: Don't Put Everything in the URL

The URL is useful for navigation and shareable state.

But don't put sensitive information into:

?token=...

or:

/users?password=...

URLs can appear in browser history, logs, analytics, server infrastructure, and referrer-related contexts.

Keep sensitive information out of URLs.


76. Best Practice: Keep Route Names Consistent

For example:

/products
/products/:id
/products/:id/reviews

is clearer than mixing unrelated naming styles.

For an application with many resources, establish conventions early.


77. Important React Router Hooks

These are some of the most important hooks to remember:

Hook Purpose
useNavigate() Programmatic navigation
useParams() Read route parameters
useSearchParams() Read/update query parameters
useLocation() Read current URL/location
useMatch() Check whether a route pattern matches
useOutlet() Access rendered child route
useOutletContext() Share context from parent route

For beginner and intermediate development, the first four are the most commonly used.


78. Important React Router Components

Component Purpose
BrowserRouter Provides browser routing context
Routes Contains route definitions
Route Defines a route
Link Navigation
NavLink Navigation with active-route information
Navigate Redirect/navigation during rendering
Outlet Renders child route
RouterProvider Runs a data router
Form Router-aware form navigation in data-router usage

79. Complete Practical Example

Let's build a small application with:

Home
Products
Product Details
About
Admin Dashboard
404

We'll also use:

  • Navigation
  • Dynamic route
  • Nested route
  • Layout
  • useParams
  • useNavigate
  • Outlet
  • 404 handling

Project structure

src/
│
├── main.tsx
│
├── App.tsx
│
├── layouts/
│   └── AdminLayout.tsx
│
├── pages/
│   ├── Home.tsx
│   ├── Products.tsx
│   ├── ProductDetails.tsx
│   ├── About.tsx
│   ├── Dashboard.tsx
│   └── NotFound.tsx
│
└── components/
    └── Navbar.tsx

main.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";

import App from "./App";

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

App.tsx

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

import Navbar from "./components/Navbar";

import Home from "./pages/Home";
import Products from "./pages/Products";
import ProductDetails from "./pages/ProductDetails";
import About from "./pages/About";
import Dashboard from "./pages/Dashboard";
import AdminLayout from "./layouts/AdminLayout";
import NotFound from "./pages/NotFound";

function App() {
  return (
    <>
      <Navbar />

      <Routes>

        <Route
          path="/"
          element={<Home />}
        />

        <Route
          path="/products"
          element={<Products />}
        />

        <Route
          path="/products/:id"
          element={<ProductDetails />}
        />

        <Route
          path="/about"
          element={<About />}
        />

        <Route
          path="/admin"
          element={<AdminLayout />}
        >
          <Route
            index
            element={<Dashboard />}
          />
        </Route>

        <Route
          path="*"
          element={<NotFound />}
        />

      </Routes>
    </>
  );
}

export default App;

Navbar.tsx

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

export default function Navbar() {

  return (
    <nav>

      <NavLink to="/">
        Home
      </NavLink>

      {" | "}

      <NavLink to="/products">
        Products
      </NavLink>

      {" | "}

      <NavLink to="/about">
        About
      </NavLink>

      {" | "}

      <NavLink to="/admin">
        Admin
      </NavLink>

    </nav>
  );
}

Products.tsx

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

const products = [
  {
    id: 101,
    name: "Laptop"
  },
  {
    id: 102,
    name: "Keyboard"
  },
  {
    id: 103,
    name: "Mouse"
  }
];

export default function Products() {

  return (
    <div>

      <h1>Products</h1>

      {products.map(product => (

        <div key={product.id}>

          <Link to={`/products/${product.id}`}>
            {product.name}
          </Link>

        </div>

      ))}

    </div>
  );
}

ProductDetails.tsx

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

export default function ProductDetails() {

  const { id } = useParams();

  const navigate = useNavigate();

  return (
    <div>

      <h1>Product Details</h1>

      <p>
        Product ID: {id}
      </p>

      <button
        onClick={() => navigate("/products")}
      >
        Back to Products
      </button>

    </div>
  );
}

AdminLayout.tsx

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

export default function AdminLayout() {

  return (
    <div>

      <h1>Admin Panel</h1>

      <nav>

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

      </nav>

      <hr />

      <Outlet />

    </div>
  );
}

Dashboard.tsx

export default function Dashboard() {

  return (
    <div>
      <h2>Dashboard</h2>

      <p>
        Welcome to Admin Dashboard.
      </p>
    </div>
  );
}

NotFound.tsx

export default function NotFound() {

  return (
    <div>

      <h1>404</h1>

      <p>
        Page Not Found
      </p>

    </div>
  );
}

80. Complete Application Flow

Now suppose the user starts at:

/

React Router matches:

<Route
  path="/"
  element={<Home />}
/>

So:

Home

is displayed.

The user clicks:

Products

React Router navigates to:

/products

The router matches:

<Route
  path="/products"
  element={<Products />}
/>

Now Products renders.

The user clicks:

Laptop

The application navigates to:

/products/101

The router matches:

<Route
  path="/products/:id"
  element={<ProductDetails />}
/>

Then:

useParams()

returns:

id = "101"

The page displays:

Product Details
Product ID: 101

This is the complete routing flow:

User clicks Product
        |
        v
<Link>
        |
        v
URL changes
/products/101
        |
        v
React Router matches
/products/:id
        |
        v
ProductDetails
        |
        v
useParams()
        |
        v
id = "101"

81. Real-World Architecture

For a real application such as an online learning platform, you could have:

/
├── courses
├── courses/:courseId
├── courses/:courseId/lessons
├── login
├── register
├── cart
├── checkout
│
└── account
    ├── profile
    ├── courses
    └── orders

And an admin area:

/admin
    ├── dashboard
    ├── courses
    ├── courses/:courseId
    ├── students
    ├── orders
    └── settings

This is where React Router becomes much more valuable than manually managing page state.


82. React Router Mental Model

The easiest way to understand React Router is to remember four questions.

1. Where am I?

Use:

useLocation()

2. What resource am I viewing?

Use:

useParams()

Example:

/products/101

means:

resource = product
id = 101

3. Where should I go?

Use:

<Link />

or:

navigate()

4. What should render here?

React Router determines this from:

<Route />

So:

URL
 ↓
Route Matching
 ↓
Component
 ↓
Component Data
 ↓
API / State
 ↓
UI

83. React Router vs React State

A very important distinction:

React state

Use state for:

Modal open/closed
Selected tab
Form values
Loading state
Temporary UI state

Router state / URL

Use routing for:

Current page
Current resource
Search/filter state that should be shareable
Pagination that should be bookmarkable

For example:

Modal:
useState()

Product ID:
URL parameter

Search:
URL query parameter

User authentication:
Context/store + backend authentication

Page navigation:
React Router

Choosing the correct place for state makes an application much easier to maintain.


84. Interview Questions and Answers

1. What is React Router?

React Router is a routing library for React applications. It maps URLs to React components and allows navigation without a full browser page reload.

2. Why is React Router used?

It provides client-side routing for SPAs, including navigation, dynamic routes, nested routes, parameters, redirects, protected routes, and layouts.

3. What is BrowserRouter?

BrowserRouter provides browser-based routing context using the browser History API.

4. What is the difference between Routes and Route?

Routes contains the route definitions, while each Route defines a URL pattern and the component to render.

5. What is Link?

Link provides client-side navigation between routes without performing a normal full-page browser navigation.

6. What is NavLink?

NavLink works like Link but also provides information about whether the current route is active.

7. What is useNavigate?

useNavigate is a hook used for programmatic navigation.

const navigate = useNavigate();

navigate("/dashboard");

8. What is useParams?

useParams reads dynamic values from the URL.

<Route path="/products/:id" element={<Product />} />
const { id } = useParams();

9. What is Outlet?

Outlet is the placeholder where a matched child route is rendered.

10. What is nested routing?

Nested routing allows child routes to render inside a parent route/layout.

Example:

/admin
/admin/users
/admin/products

11. What is a protected route?

A protected route prevents unauthorized users from accessing certain UI routes and commonly redirects them to login or an unauthorized page.

12. Is React Router enough for security?

No. React Router provides frontend navigation control. Backend APIs must independently enforce authentication and authorization.

13. What is the difference between Navigate and useNavigate?

useNavigate() is used programmatically inside component logic. <Navigate /> performs navigation through rendering.

14. What is a dynamic route?

A route containing a parameter such as:

/products/:id

which can match:

/products/101
/products/102

15. What is the difference between route parameters and query parameters?

Route parameters identify resources:

/products/101

Query parameters usually represent filtering, searching, sorting, or pagination:

/products?category=laptop&page=2

16. What is an index route?

An index route is the default child route of a parent route.

<Route index element={<Dashboard />} />

17. Why use Outlet?

It allows a parent layout to render the currently matched child route without duplicating layout code.

18. How do you create a 404 page?

Use a wildcard route:

<Route path="*" element={<NotFound />} />

19. How do you navigate after login?

Usually:

navigate("/dashboard", {
  replace: true
});

20. How does React Router work with API calls?

The route can provide identifiers such as:

/products/101

The component reads the ID with useParams() and uses it to request:

GET /api/products/101

21. Can React Router replace Redux?

No. They solve different problems. React Router handles navigation and URL state, while Redux manages application state.

22. What is lazy loading in routing?

It means loading a page's JavaScript only when that route is needed, reducing the initial bundle size.

23. What is useLocation()?

It provides information about the current URL/location, such as pathname, search parameters, and hash.

24. Can React Router protect API endpoints?

No. API protection must be implemented on the server.

25. Why use query parameters?

They are useful for URL-shareable state such as:

search
filter
sort
page

For example:

/products?search=laptop&page=2

85. Important Points to Remember

BrowserRouter
    |
    v
Routes
    |
    +---- Route
    |       |
    |       +---- Component
    |
    +---- Nested Route
            |
            +---- Outlet

Remember these core concepts:

Concept Main Purpose
BrowserRouter Enable browser routing
Routes Manage routes
Route Map URL to component
Link Navigate
NavLink Navigate + active state
useNavigate Programmatic navigation
useParams Read URL parameters
useSearchParams Read/update query parameters
useLocation Read current location
Outlet Render child routes
Navigate Redirect
index route Default child route
* route Catch unmatched URLs
lazy + Suspense Lazy-load route components
loader Route-level data loading
action Route-level data mutation

86. The Most Important Mental Model

If you remember only one thing about React Router, remember this:

                URL
                 |
                 v
        React Router
                 |
                 v
          Route Matching
                 |
                 v
        React Component
                 |
        +--------+--------+
        |                 |
        v                 v
    Route Data       Application State
        |                 |
        +--------+--------+
                 |
                 v
                UI

For example:

/products/101?tab=reviews

can be understood as:

/products
     |
     +-- :id = 101
     |
     +-- tab = reviews

Then:

useParams()

can provide:

id = "101"

and:

useSearchParams()

can provide:

tab = "reviews"

Your component can then use those values to determine what data and UI to display.


87. Summary

React Router is the routing system that allows a React application to support multiple URL-based views while remaining a Single Page Application.

The most important concepts are:

BrowserRouter
      ↓
Routes
      ↓
Route
      ↓
Component

For navigation:

<Link />
<NavLink />
useNavigate()

For reading URL information:

useParams()
useSearchParams()
useLocation()

For nested applications:

Outlet
Nested Routes
Layout Routes
Index Routes

For real-world applications:

Authentication
Protected Routes
Role-based Routes
Dynamic Routes
Query Parameters
Lazy Loading
Error Handling
Route-level Data Loading

The key design principle is:

Use React Router to represent navigation and URL state; use React state, Context, Redux, or other state-management tools for application state; and always enforce security on the backend as well.

Once you understand routes → navigation → parameters → nested routes → layouts → protected routes, you have the foundation needed to build routing for most real-world React applications.

← Back to Articles