Home/Learn/React/React Router — Routes, Links and Layouts

React Router — Routes, Links and Layouts

Beginner
Routing

Routes map URLs to components, Link navigates without a reload, and nested routes let a layout stay mounted while only the inner part changes.

Overview

React has no router, so a single-page app needs one, and React Router is what most teams use outside Next.js. The mechanics sit on the History API from the JavaScript track: the router pushes a URL, matches it against your route definitions, and renders the matching component. The part worth learning properly is nesting, because it maps directly onto how applications are actually laid out — a shell with navigation that stays put while the content area changes, without re-mounting the shell on every navigation.

Defining Routes

A route tree, with layouts as parent routes.

Nested routes and Outlet
const router = createBrowserRouter([
  {
    path: '/',
    element: <RootLayout />,          // header, nav, footer — stays mounted
    errorElement: <ErrorPage />,
    children: [
      { index: true, element: <Home /> },
      { path: 'problems', element: <ProblemList /> },
      { path: 'problems/:slug', element: <ProblemDetail /> },
      {
        path: 'account',
        element: <AccountLayout />,   // a nested layout with its own tabs
        children: [
          { index: true, element: <Profile /> },
          { path: 'billing', element: <Billing /> },
        ],
      },
      { path: '*', element: <NotFound /> },     // 404, always include one
    ],
  },
])

<RouterProvider router={router} />

// The layout renders <Outlet /> where the child route goes
function RootLayout() {
  return <><Header /><main><Outlet /></main><Footer /></>
}

Navigating

Link for user navigation, navigate for programmatic. Never an anchor to an internal route.

Link, NavLink, useNavigate
<Link to="/problems">Problems</Link>
<Link to={`/problems/${slug}`} state={{ from: 'list' }}>Open</Link>

// NavLink knows whether it is the current route
<NavLink to="/problems" className={({ isActive }) => isActive ? 'on' : ''}>

// A plain <a href="/problems"> triggers a full page reload —
// losing all state and re-downloading the app.

// Programmatic navigation, after an action
const navigate = useNavigate()
await createProblem(values)
navigate(`/problems/${created.slug}`)
navigate(-1)                                   // back
navigate('/login', { replace: true })          // no history entry
// replace matters after a redirect: without it, pressing back
// returns the user to the page that just bounced them.

// Reading where you are
const { slug } = useParams()
const location = useLocation()      // pathname, search, state

Data and Errors per Route

Loaders fetch before the route renders; errorElement catches what fails.

loader, useLoaderData, errorElement
{
  path: 'problems/:slug',
  element: <ProblemDetail />,
  loader: ({ params, request }) =>
    getProblem(params.slug, { signal: request.signal }),
  errorElement: <RouteError />,
}

function ProblemDetail() {
  const problem = useLoaderData()      // already resolved — no loading state
  return <Article problem={problem} />
}

// Loaders fetch in parallel with the route transition rather than
// after it, which removes the render-then-fetch waterfall.

// errorElement catches a thrown loader error or a render error in
// that subtree, so one broken route does not blank the whole app.
const error = useRouteError()

// Many teams use loaders for the initial fetch and TanStack Query
// for everything after — the two compose, and the query cache can
// be seeded from the loader.

Key Points to Remember

  • 1Nested routes keep a layout mounted while only the inner Outlet changes on navigation
  • 2Link and NavLink navigate client-side; a plain anchor to an internal route reloads the whole app
  • 3navigate(path, { replace: true }) avoids a history entry, which matters after a redirect
  • 4Always define a catch-all route so an unknown URL renders a real 404 page
  • 5Loaders fetch during the transition rather than after render, and errorElement contains failures to one subtree

Interview Questions

Sign in to ask Aria
1

What happens if you use a plain <a> tag to link between routes in a single-page app?

Easy
2

What is an Outlet and why do nested routes matter?

Medium
3

When would you navigate with replace: true?

Medium

Ask Aria about React Router — Routes, Links and Layouts

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…