Modern React 23: React Router v7
This is part 23 of the Modern React development series.
React Router v7 can be used as a routing library or as a framework-style setup with routes, loaders, actions, and server rendering options. Its core model keeps URL state, route data, and UI nesting close together.
Concept
React Router maps locations to route modules. A route module can define UI, data loading, mutations, and nested child routes, so navigation can fetch the data needed for the next screen before the component renders.
Terms
- Route module: A file or route definition that owns a route’s component and optional data APIs.
- Loader: A function that loads data for a route before rendering.
- Action: A function that handles route mutations, often from form submissions.
- Nested route: A child route rendered inside a parent route outlet.
Mental model
Think of each route as a station. The router knows which station the URL points to, what data should be waiting there, and which parent stations stay on screen around it.
How it is used
Use React Router for applications where routes, nested layouts, forms, and route data are central, especially when the app should be built on web platform primitives and can choose between library and framework modes.
How to use it
- Define routes around the URL structure users will understand.
- Use nested routes for UI that shares a parent layout.
- Load route data in loaders when the router should own the fetch timing.
- Use actions for mutations tied to navigation or form submissions.
- Keep route params and search params typed or validated at the route boundary.
Example: Route with loader data
import type { LoaderFunctionArgs } from "react-router";import { getProject } from "./projects";
export async function loader({ params }: LoaderFunctionArgs) { return getProject(String(params.projectId));}
export default function ProjectRoute({ loaderData,}: { loaderData: Awaited<ReturnType<typeof loader>>;}) { return ( <main> <h1>{loaderData.name}</h1> <p>{loaderData.description}</p> </main> );}Launch plan
Coordinate release tasks before the public launch.
The route owns the data required to render the route screen.
Example: Nested layout with an outlet
import { Outlet } from "react-router";
export default function AccountLayout() { return ( <section> <nav aria-label="Account"> <a href="/account/profile">Profile</a> <a href="/account/security">Security</a> </nav> <Outlet /> </section> );}The parent route renders the shared account shell while child routes fill the outlet.
Details to watch
- URL as state: Route params and search params are shareable state. Treat them as part of the app contract.
- Loader timing: Loader-based data avoids every component inventing its own fetch Effect.
- Actions: Route actions fit mutations that naturally belong to a route or form.
- Mode choice: React Router can be used in different modes. Match the mode to the app’s routing and data needs.
Series navigation
- Previous: Part 22: Next.js App Router
- Next: Part 24: TanStack Router and TanStack Start
- Series index: Modern React development