Routing

This section documents React 19.x for the web, using function components and Hooks throughout (class components appear only where React still requires them, such as error boundaries). React Native is out of scope and gets only a pointer. The React 19 additions follow the current official React documentation. This content was generated with the assistance of AI and should be verified against react.dev before being relied on in production, since React APIs continue to evolve between releases.

This section’s bibliography lists the reference material consulted while preparing these pages.

React itself has no router. Client-side routing — mapping the URL to which components render, without a full page reload — is provided by a library. This page is a short orientation; follow the links for the full reference.

The options

Library router vs framework router

A library router is a package you add to a client-rendered app, and you wire data loading, bundling and rendering yourself. A framework router is part of a framework that owns the build, the server and the route conventions. React Router spans both: its three modes are additive.

Router Route definition Data loading Code splitting SSR Nested layouts

React Router — declarative mode

JSX (<Routes> / <Route>) inside <BrowserRouter>

None built in; bring your own (Effects, TanStack Query)

Manual (lazy + <Suspense>)

Manual, with StaticRouter on your own server

Yes, <Outlet>

React Router — data mode

Route objects passed to createBrowserRouter + <RouterProvider>

loader, action, useFetcher, pending UI

Route lazy (manual)

Manual, with createStaticHandler and StaticRouterProvider on your own server

Yes, nested children + <Outlet>

React Router — framework mode

routes.ts config (or file conventions) with route modules

Route module loader / action with type-safe loaderData

Automatic, per route

SPA, SSR and static pre-rendering strategies

Yes

TanStack Router

Code-based routes or file-based routes (generated route tree), with type-safe params and search params

Route loader with built-in caching and preloading

Automatic (autoCodeSplitting in the bundler plugin)

Documented, but marked experimental while TanStack Start is not stable

Yes

Next.js App Router (details)

File-system: folders with page files under app/

async Server Components, fetch on the server

Automatic, per route segment

Yes: static, dynamic and streaming rendering by default

Yes, layout files

Expo Router

File-system: files under src/app/

Data loaders (web)

Async routes on web (stable and on by default from SDK 58; experimental on native)

Static rendering and server rendering on web (server rendering stable from SDK 58)

Yes, _layout files

With React Router, the mode is chosen by the top-level API you use (BrowserRouter, createBrowserRouter, or the Vite plugin), and every example below imports from the react-router package. Since React Router 7 the former react-router-dom package is merged into react-router; react-router-dom is only a thin re-export of it (its latest release is still 7.x), and the current react-router major version is 8.

Minimal examples (React Router)

Declaring routes, including a nested route and a lazy one:

import { createBrowserRouter, RouterProvider } from 'react-router';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />,
    children: [
      { index: true, element: <Home /> },
      { path: 'products/:id', element: <Product /> },
      { path: 'reports', lazy: () => import('./routes/reports.jsx') },
    ],
  },
]);

<RouterProvider router={router} />;

Route params and query params:

import { useParams, useSearchParams } from 'react-router';

function Product() {
  const { id } = useParams();                 // "products/:id"
  const [searchParams] = useSearchParams();
  const tab = searchParams.get('tab') ?? 'overview';   // ?tab=specs
  return <h1>Product {id} — {tab}</h1>;
}

Links and programmatic navigation:

import { Link, NavLink, useNavigate } from 'react-router';

<Link to="/products/42">View</Link>
<NavLink to="/reports" className={({ isActive }) => (isActive ? 'on' : undefined)}>
  Reports
</NavLink>;

function SaveButton() {
  const navigate = useNavigate();
  return <button onClick={() => navigate('/products/42')}>Go</button>;
}

Lazy routes pair with code splitting: each route’s component is its own chunk, downloaded on first visit. For everything else — data loading, nested layouts, error elements, scroll restoration — see reactrouter.com, tanstack.com/router, or the Next.js docs (and Next.js routing fundamentals in this reference).