ReactRoutes, Links & Navigation

Routes, Links & Navigation

React itself has no built-in router. React Router v6 is the standard choice for client-side routing in most React apps. It maps URL paths to components, enables navigation without full page reloads, and provides hooks for reading and updating the URL.

Install React Router with:

Bash
npm install react-router-dom
Wrapping Your App in a Router

Wrap your root component in BrowserRouter (or RouterProvider with the newer data router API). All routing hooks and components must be inside this context:

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

function Root() {
  return (
    <BrowserRouter>
      <App />
    </BrowserRouter>
  )
}
Routes and Route

Routes is the container that looks at the current URL and renders the first matching Route. Each Route maps a path to an element:

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

function App() {
  return (
    <Routes>
      <Route path="/"         element={<Home />} />
      <Route path="/about"    element={<About />} />
      <Route path="/blog"     element={<Blog />} />
      <Route path="/contact"  element={<Contact />} />
      {/* Catch-all: shown when nothing else matches */}
      <Route path="*"         element={<NotFound />} />
    </Routes>
  )
}
Note
In React Router v6, Routes always picks the best match — not just the first match. You do not need the 'exact' prop that was required in v5.
Index Routes

An index route is the default child rendered when the parent path matches but no child path does. Add the index prop instead of a path:

TSX
<Routes>
  <Route path="/dashboard" element={<DashboardLayout />}>
    <Route index element={<DashboardHome />} />    {/* /dashboard */}
    <Route path="stats"   element={<Stats />} />  {/* /dashboard/stats */}
    <Route path="reports" element={<Reports />} />{/* /dashboard/reports */}
  </Route>
</Routes>
Link — Navigation Without Reloads

Never use a plain <a href> for internal navigation — it causes a full page reload and loses React state. Use Link from React Router instead:

TSX
import { Link } from 'react-router-dom'

function Nav() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/blog">Blog</Link>
      <Link to="/contact">Contact</Link>
    </nav>
  )
}
NavLink — Active Styling

NavLink extends Link with automatic active-state awareness. It adds an active class (or applies an inline style) when the current URL matches the link's to path:

TSX
import { NavLink } from 'react-router-dom'

function MainNav() {
  return (
    <nav>
      {/* className can be a function that receives { isActive } */}
      <NavLink
        to="/"
        className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
        end                    // 'end' prevents '/' matching every path
      >
        Home
      </NavLink>

      <NavLink
        to="/blog"
        style={({ isActive }) => ({
          fontWeight: isActive ? 'bold' : 'normal',
          color: isActive ? 'tomato' : 'inherit',
        })}
      >
        Blog
      </NavLink>
    </nav>
  )
}
Warning
Without the 'end' prop, the root NavLink (to="/") is always active because every URL starts with "/". Add end to make it active only on the exact root path.
Navigate — Declarative Redirects

The Navigate component redirects as part of rendering — useful for conditional redirects based on auth or derived state:

TSX
import { Navigate } from 'react-router-dom'

function Dashboard() {
  const { isLoggedIn } = useAuth()

  if (!isLoggedIn) {
    // Redirect to /login without leaving a history entry
    return <Navigate to="/login" replace />
  }

  return <DashboardContent />
}
Complete Multi-Page App

TSX
import { BrowserRouter, Routes, Route, NavLink, Outlet } from 'react-router-dom'

// Pages
function Home()    { return <h1>Home</h1> }
function About()   { return <h1>About</h1> }
function Blog()    { return <h1>Blog</h1> }
function NotFound(){ return <h1>404 — Page not found</h1> }

// Shared navigation layout
function Layout() {
  return (
    <div>
      <header>
        <nav style={{ display: 'flex', gap: 16 }}>
          <NavLink to="/" end className={({ isActive }) => isActive ? 'active' : ''}>
            Home
          </NavLink>
          <NavLink to="/about" className={({ isActive }) => isActive ? 'active' : ''}>
            About
          </NavLink>
          <NavLink to="/blog" className={({ isActive }) => isActive ? 'active' : ''}>
            Blog
          </NavLink>
        </nav>
      </header>

      <main>
        {/* Child routes render here */}
        <Outlet />
      </main>
    </div>
  )
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="blog"  element={<Blog />} />
          <Route path="*"     element={<NotFound />} />
        </Route>
      </Routes>
    </BrowserRouter>
  )
}
Relative vs Absolute Paths
  • to="/about" — absolute path, always navigates to /about.

  • to="about" — relative path, appended to the current route segment.

  • to="../settings" — go up one segment, then to settings.

  • Inside nested routes, prefer relative paths so parent path changes do not break child links.

Tip
The Layout route pattern (a Route with no path, only an element) is the cleanest way to share a header and footer across all pages. Nest all your page routes inside it and render child pages via <Outlet />.