Form component for URLSearchParams navigation
The Form component can be used to navigate with URLSearchParams. When a user submits a form with <Form action="/search"> and an input with name="q", they navigate to the action URL with query parameters, for example /search?q=journey.
Form POST method with FormData
Forms with <Form method="post" /> will submit data as FormData instead of URLSearchParams. It is more common to use useFetcher() to POST form data.
useNavigate hook for programmatic navigation
The useNavigate hook allows programmatic navigation without user interaction. Usage should be uncommon and reserved for situations where the user is not interacting, such as logging them out after inactivity or handling timed UIs like quizzes.
NavLink className callback example
Example: <NavLink to="/messages" className={({ isActive, isPending, isTransitioning }) => [isPending ? "pending" : "", isActive ? "active" : "", isTransitioning ? "transitioning" : ""].join(" ")} >Messages</NavLink> - This shows how to conditionally set CSS classes based on link state.
NavLink style callback example
Example: <NavLink to="/messages" style={({ isActive, isPending, isTransitioning }) => { return { fontWeight: isActive ? "bold" : "", color: isPending ? "red" : "black", viewTransitionName: isTransitioning ? "slide" : "" }; }} >Messages</NavLink> - This shows how to conditionally set inline styles based on link state.
NavLink children callback example
Example: <NavLink to="/tasks">{({ isActive, isPending, isTransitioning }) => (<span className={isActive ? "active" : ""}>Tasks</span>)}</NavLink> - This shows how to conditionally render content based on link state.
Form for search navigation example
Example: <Form action="/search"><input type="text" name="q" /></Form> - When user enters "journey" and submits, they navigate to /search?q=journey.
redirect in loader example
Example: export async function loader({ request }) { let user = await getUser(request); if (!user) { return redirect("/login"); } return { userName: user.name }; } - This shows how to redirect unauthenticated users from a loader.
redirect after record creation example
Example: export async function action({ request }) { let formData = await request.formData(); let project = await createProject(formData); return redirect(`/projects/${project.id}`); } - This shows how to redirect to a new record URL after creation in an action.
useNavigate hook example
Example: export function useLogoutAfterInactivity() { let navigate = useNavigate(); useFakeInactivityHook(() => { navigate("/logout"); }); } - This shows how to use useNavigate to programmatically navigate based on an event.
Route module loader receives params
A route module's loader function receives params from dynamic segments in its route pattern. For example, a route "teams/:teamId" provides params.teamId as a string to the loader.
Route module component receives loaderData
A route module's default component function receives loaderData from the loader, which contains the data returned by the loader function.
Route module type safety with +types
Route modules can import type information from ./+types/[routeName] for type safety and inference of loaderData and other route properties.
Route module basic structure example
A basic route module imports types, exports a loader function that receives params and returns data, and exports a default component function that receives loaderData. The component renders after the loader completes.
Routes configuration example
Example route configuration showing index(), route(), layout(), and prefix() functions:
```ts
export default [
index("./home.tsx"),
route("about", "./about.tsx"),
layout("./auth/layout.tsx", [
route("login", "./auth/login.tsx"),
route("register", "./auth/register.tsx"),
]),
...prefix("concerts", [
index("./concerts/home.tsx"),
route(":city", "./concerts/city.tsx"),
route("trending", "./concerts/trending.tsx"),
]),
] satisfies RouteConfig;
```
Index routes render at parent URL
Index routes render into their parent's Outlet at their parent's URL, acting as a default child route. They are created using the index() function and cannot have children.
Dynamic segments start with colon
A path segment that starts with ":" becomes a dynamic segment. When the route matches a URL, the dynamic segment is parsed from the URL and provided as params to router APIs.
Route function syntax
Routes are defined using the route() function imported from @react-router/dev/routes. The syntax is route("path/pattern", "./module-file.tsx").
Routes configured in app/routes.ts
Routes are configured in app/routes.ts. Each route requires two parts: a URL pattern to match the URL, and a file path to the route module that defines its behavior.
Prefix function adds path segment without creating route
The prefix() function adds a path prefix to a set of routes without introducing a new parent route into the route tree. It only modifies the paths of its children.
Root route contains all routes
Every route in routes.ts is nested inside the special app/root.tsx module, which serves as the root route for the entire application.
Outlet component renders child routes
Child routes are rendered through the <Outlet/> component in the parent route module. This is where nested route content appears.
Nested routes include parent path
Child routes nested inside parent routes automatically include the parent's path. For example, nesting a route for "settings" inside a "dashboard" parent creates the URL "/dashboard/settings".
Layout routes create nesting without URL segments
Layout routes use the layout() function to create new nesting for their children without adding any segments to the URL. They work like the root route but can be added at any level.
Multiple dynamic segments in one route
A route can have multiple dynamic segments. For example, route("c/:categoryId/p/:productId", "./product.tsx") creates a route with two dynamic segments that both become available in params.
Optional segments with question mark
Route segments can be made optional by adding a "?" to the end of the segment. For example, route(":lang?/categories", "./categories.tsx") makes the lang segment optional. Optional segments can be static or dynamic.
Splat segments catch remaining URL path
Also known as catchall or star segments. If a route path pattern ends with "/*" it will match any characters following the "/", including other "/" characters. The matched portion is available in params["*"].
Splat can catch unmatched routes
A route with path "*" can be used as a catchall route to match requests that don't match any other route, commonly used for 404 handling.
Component routes are limited
Component routes using <Routes> and <Route> elements can match URLs to components anywhere in the component tree, but they do not participate in data loading, actions, code splitting, or other route module features, so their use cases are more limited than route modules.
File system routing alternative
The @react-router/fs-routes package provides file system routing conventions as an alternative to manual route configuration. It can be combined with manual routes using the flatRoutes() function.
Route meta with React 19 example
Example using built-in meta and title elements (recommended for React 19+):
```tsx
export default function MyRoute() {
return (
<div>
<title>Very cool app</title>
<meta property="og:title" content="Very cool app" />
<meta
name="description"
content="This app is the best"
/>
{/* The rest of your route content... */}
</div>
);
}
```
Route Module definition
Route modules are files referenced in routes.ts that define how a route behaves. They are the foundation of React Router's framework features and can define automatic code-splitting, data loading, actions, revalidation, error boundaries, and more.
Route Module default export
The default export in a route module defines the component that renders when the route matches. This is a required export for a functioning route.
Route Component props
Route components receive props from Route.ComponentProps including loaderData (data from the loader function), actionData (data from the action function), params (route parameters as an object), and matches (array of all matches in the current route tree). These props can be used instead of hooks like useLoaderData or useParams and are automatically typed correctly for the route.
Route middleware
Route middleware runs sequentially on the server before and after document and data requests. It provides a singular place for logging, authentication, and post-processing responses. The next function continues down the chain, and on the leaf route it executes loaders/actions for navigation.
Route clientMiddleware
clientMiddleware is the client-side equivalent of middleware and runs in the browser during client navigations. Unlike server middleware, client middleware does not return Responses because it is not wrapping an HTTP request on the server.
Route ErrorBoundary export
When other route module APIs throw an error, the route module ErrorBoundary will render instead of the route component. It receives the error via useRouteError hook and can check if it is a RouteErrorResponse using isRouteErrorResponse helper.
Route HydrateFallback export
On initial page load, the route component renders only after the client loader finishes. If a HydrateFallback is exported, it renders immediately in place of the route component while the client loader is running.
Route headers function
The route headers function defines HTTP headers to be sent with the response when server rendering. It returns an object with header names as keys and header values as values.
Route handle export
Route handle allows apps to add anything to a route match that can be accessed via useMatches hook to create abstractions like breadcrumbs. It is a simple object that you define.
Route links function
Route links function defines link elements to be rendered in the document head. It returns an array of link objects with properties like rel, href, type, and as. All route links are aggregated and rendered through the Links component placed in the app root.
Route meta function
Route meta defines meta tags to be rendered in the Meta component, usually placed in the head. The meta of the last matching route is used, allowing you to override parent routes' meta. The entire meta descriptor array is replaced, not merged, giving flexibility to build custom meta composition logic. Since React 19, using the built-in meta and title elements directly in the component is recommended over the meta export.
Route shouldRevalidate function
In framework mode with SSR, route loaders are automatically revalidated after all navigations and form submissions. The shouldRevalidate function allows you to opt out of revalidation for a route loader for specific navigations and form submissions. When using SPA Mode, shouldRevalidate behaves the same as in Data Mode since there are no server loaders to call on navigations.
Route module auth middleware example
Example middleware to check for logged in users and set the user in context:
```tsx
async function authMiddleware({ request, context }) {
const session = await getSession(request);
const userId = session.get("userId");
if (!userId) {
throw redirect("/login");
}
const user = await getUserById(userId);
context.set(userContext, user);
}
export const middleware = [authMiddleware];
```
Route component with props example
Example route component using Route.ComponentProps:
```tsx
import type { Route } from "./+types/route-name";
export default function MyRouteComponent({
loaderData,
actionData,
params,
matches,
}: Route.ComponentProps) {
return (
<div>
<h1>Welcome to My Route with Props!</h1>
<p>Loader Data: {JSON.stringify(loaderData)}</p>
<p>Action Data: {JSON.stringify(actionData)}</p>
<p>Route Parameters: {JSON.stringify(params)}</p>
<p>Matched Routes: {JSON.stringify(matches)}</p>
</div>
);
}
```
Route client middleware logging example
Example middleware to log requests on the client:
```tsx
async function loggingMiddleware(
{ request, context },
next,
) {
console.log(
`${new Date().toISOString()} ${request.method} ${request.url}`,
);
const start = performance.now();
await next(); // 👈 No Response returned
const duration = performance.now() - start;
console.log(
`${new Date().toISOString()} (${duration}ms)`,
);
// ✅ No need to return anything
}
export const clientMiddleware = [loggingMiddleware];
```
Route ErrorBoundary example
Example route ErrorBoundary:
```tsx
import {
isRouteErrorResponse,
useRouteError,
} from "react-router";
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return (
<div>
<h1>
{error.status} {error.statusText}
</h1>
<p>{error.data}</p>
</div>
);
} else if (error instanceof Error) {
return (
<div>
<h1>Error</h1>
<p>{error.message}</p>
<p>The stack trace is:</p>
<pre>{error.stack}</pre>
</div>
);
} else {
return <h1>Unknown Error</h1>;
}
}
```
Route HydrateFallback example
Example route with HydrateFallback:
```tsx
export async function clientLoader() {
const data = await fakeLoadLocalGameData();
return data;
}
export function HydrateFallback() {
return <p>Loading Game...</p>;
}
export default function Component({ loaderData }) {
return <Game data={loaderData} />;
}
```
Route headers example
Example route headers function:
```tsx
export function headers() {
return {
"X-Stretchy-Pants": "its for fun",
"Cache-Control": "max-age=300, s-maxage=3600",
};
}
```
Route handle example
Example route handle:
```tsx
export const handle = {
its: "all yours",
};
```
Route links example
Example route links function:
```tsx
export function links() {
return [
{
rel: "icon",
href: "/favicon.png",
type: "image/png",
},
{
rel: "stylesheet",
href: "https://example.com/some/styles.css",
},
{
rel: "preload",
href: "/images/banner.jpg",
as: "image",
},
];
}
```
All route links are aggregated and rendered through the Links component in your app root.
Route meta function example
Example route meta function (pre React 19 approach):
```tsx
// app/product.tsx
export function meta() {
return [
{ title: "Very cool app" },
{
property: "og:title",
content: "Very cool app",
},
{
name: "description",
content: "This app is the best",
},
];
}
```
Used with Meta component in root:
```tsx
// app/root.tsx
import { Meta } from "react-router";
export default function Root() {
return (
<html>
<head>
<Meta />
</head>
<body />
</html>
);
}
```
Route shouldRevalidate example
Example shouldRevalidate function:
```tsx
import type { ShouldRevalidateFunctionArgs } from "react-router";
export function shouldRevalidate(
arg: ShouldRevalidateFunctionArgs,
) {
return true;
}
```
When to use Framework mode
Use Framework Mode if you are new to routing, considering Next.js or similar frameworks and want to compare, just want to build something with React, might want server rendering or might not, or are migrating from Next.js.
Framework mode route configuration example
```ts
import { index, route } from "@react-router/dev/routes";
export default [
index("./home.tsx"),
route("products/:pid", "./product.tsx"),
];
```
This is an example of Framework mode route configuration in a routes.ts file.
Framework mode features
Framework Mode wraps Data Mode with a Vite plugin to add the full React Router experience with type-safe href, type-safe Route Module API, intelligent code splitting, SPA, SSR, and static rendering strategies, and more.
Three primary modes in React Router
React Router has three primary modes: Declarative, Data, and Framework. The features are additive, so moving from Declarative to Data to Framework adds more features at the cost of architectural control. The mode depends on which top-level router API you're using.
When to use Declarative mode
Use Declarative Mode if you want to use React Router as simply as possible, are coming from earlier React Router versions and are happy with BrowserRouter, have a data layer that either skips pending states or has its own abstractions for them, or are coming from Create React App.
API availability across modes - comprehensive table
React Router APIs have varying support across the three modes. Framework mode supports all APIs. Data mode supports most APIs but not Link discover, Link prefetch, NavLink discover, NavLink prefetch, Meta, Scripts, PrefetchPageLinks, or href. Declarative mode has limited support: Link, NavLink, Navigate, Outlet, Route, Routes, useBeforeUnload, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes, useSearchParams, createPath, createSearchParams, generatePath, matchPath, matchRoutes, parsePath, renderMatches, and resolvePath. APIs unique to Data and Framework modes include Await, Form, preventScrollReset properties, useActionData, useAsyncError, useAsyncValue, useBlocker, useFetcher, useFetchers, useFormAction, useLoaderData, useMatches, useNavigation, usePrompt, useRevalidator, useRouteError, useRouteLoaderData, useSubmit, useViewTransitionState, session and cookie functions, redirect, redirectDocument, and replace.
Declarative mode basic features
Declarative mode enables basic routing features like matching URLs to components, navigating around the app, and providing active states. It uses APIs like <Link>, useNavigate, and useLocation. To use it, import BrowserRouter from react-router and wrap your app with it.