route.lazy cannot override statically defined properties
If a data-loading or rendering property (loader, action, hasErrorBoundary, shouldRevalidate, element, errorElement, Component, ErrorBoundary, or handle) is statically defined on a route, the route.lazy() function cannot override it. The router logs a console warning if you attempt to do so.
route.Component and route.ErrorBoundary fields
React Router supports route.Component and route.ErrorBoundary as alternatives to route.element and route.errorElement. Component is a reference to a component function (not a JSX element), which allows using hooks inline and simplifies lazy route exports. ErrorBoundary is a reference to an error boundary component. If both Component and element are defined, Component takes precedence.
lazy route module exports
A lazy route module can export loader, action, Component, ErrorBoundary, and other route properties. These exports are applied to the route definition after the lazy() promise resolves. The lazy function returns a module with these exported properties.
Parallel execution of static and lazy properties
If a route has both a statically-defined loader or action and a lazy() function, React Router detects this and calls the static loader/action in parallel with the lazy() call. This provides optimal parallelization of loading the component in parallel with data fetches.
lazy() interruption behavior
If a user navigates away (interrupts) before a lazy() promise resolves, React Router will still call the returned handler from the lazy() function. This ensures consistent behavior between first navigation and subsequent navigations. Handlers can use request.signal.aborted to short-circuit on interruption if desired.
Multiple parallel lazy() calls to same route
If multiple navigations happen to the same route in parallel, the first lazy() call to resolve will 'win' and update the route. The returned values from other lazy() executions will be ignored. Modern bundlers latch onto the same promise for repeated import() calls so in practice the first call still wins.
lazy() error handling
If an error is thrown by a lazy() function, it is caught by the router in the same logic as if the error was thrown by a loader or action, and the error bubbles to the nearest errorElement.
lazy() basic usage example
Here's a basic example of lazy route loading:
```jsx
// app.jsx
const router = createBrowserRouter([
{
path: "/",
Component: Layout,
children: [
{
index: true,
Component: Home,
},
{
path: "about",
lazy: () => import("./about"),
},
],
},
]);
// about.jsx
export function loader() { ... }
export function Component() { ... }
```
This example shows loading the homepage in the main bundle but lazily loading the code for the `/about` route. The about.jsx file exports the loader and Component properties.
route.lazy cannot update path matching properties
Path matching properties (path, index, caseSensitive, children, and id) are immutable and cannot be updated by route.lazy(). The router logs a warning if a lazy() method returns any of these properties. Path matching must occur before lazy loading, so these properties must be statically defined.
route.lazy function purpose and when it's called
The route.lazy property is a function that lazily loads data loading and rendering properties of a route. It is called when entering a submitting or loading state during navigation. The router first checks for a route.lazy definition and resolves that promise before updating the internal route definition with the result. Once lazy has completed, subsequent navigations do not repeat the lazy() call because the route definition is statically updated in place.
Route exports API remains unchanged
Route modules continue to export separate values for loader, clientLoader, action, ErrorBoundary, and default component exports. This preserves compatibility with standard tooling like tree-shaking and React Fast Refresh (HMR), and allows Remix users to upgrade to React Router v7 without breaking changes.
Route discovery configuration in react-router.config.ts
Route discovery behavior is configured in react-router.config.ts via the routeDiscovery object. The routeDiscovery configuration can have: mode set to "lazy" (default) or "initial", and an optional manifestPath property. Default configuration is routeDiscovery: { mode: "lazy", manifestPath: "/__manifest" }. Setting mode to "initial" disables lazy discovery and includes all routes initially. A custom manifestPath can be set, which is useful for running multiple React Router applications on the same domain.
RouteConfig type and route configuration
RouteConfig is a type that defines route configuration. Routes are defined using the route() function and exported as an array satisfying RouteConfig. Example: route("products/:id", "./routes/product.tsx") defines a route with a path and a corresponding file. The configuration is typically in app/routes.ts.
Route.ActionArgs type parameter
Route.ActionArgs is a typed parameter object for action functions in React Router that contains the request property. It is accessed as the first parameter to the action function and provides type safety for TypeScript projects.
flatRoutes ignoredRouteFiles option
The flatRoutes function accepts an ignoredRouteFiles option that takes an array of filenames to exclude from becoming routes. Example: flatRoutes({ ignoredRouteFiles: ['home.tsx'] })
flatRoutes function setup
The flatRoutes function is imported from @react-router/fs-routes and exported as default from app/routes.ts, satisfying the RouteConfig type. It generates route configuration from files in the app/routes directory by default.
flatRoutes rootDirectory option
The flatRoutes function accepts a rootDirectory option that specifies the directory path relative to the app directory where routes should be discovered. Default is 'app/routes'. Example: flatRoutes({ rootDirectory: 'file-routes' })
Route object middleware property
The middleware property is an array of middleware functions that run sequentially before and after navigations. Middleware receives an object with request and context, and calls the next function to continue the chain. On the leaf route, calling next executes loaders/actions for the navigation. Middleware can be used for logging, authentication, and other cross-cutting concerns.
Route object passed to createBrowserRouter
Route objects are passed to createBrowserRouter and define the configuration for each route in the application. A basic route object requires a path property (string) and a Component property (React component).
Route object Component property
The Component property in a route object specifies the React component that will render when the route matches. It accepts a React component function or class component.
Route object action property
The action property is an async function that allows server-side data mutations. When called from Form, useFetcher, or useSubmit components, actions automatically trigger revalidation of all loader data on the page. The action receives an object with a request property (FormData from request.formData()).
Route object lazy property
The lazy property allows most route object properties to be lazily imported to reduce initial bundle size. It is an async function that returns an object with properties like Component and loader. These are loaded in parallel before the route renders.
Route object handle property
The handle property allows apps to add arbitrary data to a route match for use in useMatches hook to create abstractions like breadcrumbs. It can contain any serializable data structure.
Middleware example with logging
Example middleware function: async function loggingMiddleware({ request }, next) { let url = new URL(request.url); console.log(`Starting navigation: ${url.pathname}${url.search}`); const start = performance.now(); await next(); const duration = performance.now() - start; console.log(`Navigation completed in ${duration}ms`); }
Middleware example with authentication
Example middleware function: async function authMiddleware ({ context }) { const userId = getUserId(); if (!userId) { throw redirect("/login"); } context.set(userContext, await getUserById(userId)); }
Action example with Form and automatic revalidation
Example action and component showing automatic revalidation: async function action({ request }) { const data = await request.formData(); const todo = await fakeDb.addItem({ title: data.get("title"), }); return { ok: true }; } async function loader() { const items = await fakeDb.getItems(); return { items }; } export default function Items() { let data = useLoaderData(); return ( <div> <List items={data.items} /> <Form method="post" navigate={false}> <input type="text" name="title" /> <button type="submit">Create Todo</button> </Form> </div> ); }
Lazy route property example
Example lazy route configuration: createBrowserRouter([ { path: "/app", lazy: async () => { const [Component, loader] = await Promise.all([ import("./app"), import("./app-loader"), ]); return { Component, loader }; }, }, ]);
Route handle example for breadcrumbs
Example route handle configuration: createBrowserRouter([ { path: "/app", handle: { breadcrumb: "App", }, }, ]);
Routes configured via routes.ts with RouteConfig type
Routes are configured in a routes.ts file using the RouteConfig type and the route function from '@react-router/dev/routes'. The route function takes a path pattern and a file path to the route module. Multiple route definitions are combined in an array that satisfies RouteConfig.
Route pattern with catchall using *? syntax
The pattern '*?' is used to create a catchall route that matches all URLs. The asterisk matches all paths and the question mark makes it optional, so it also matches the root path '/'.
MDX route meta export
In an MDX route file, you can export a meta function that returns an array of objects. The meta function can define route metadata such as the page title. Example: export const meta = () => [{ title: "MDX Route" }];
MDX route component structure
An MDX route file can contain both ES module exports (like meta and loader functions) and MDX content (markdown and JSX) in the same file. React components can be imported and used within the MDX content.
Route module definition and purpose
Route modules are files referenced in routes.ts that define the component and data handling for a route. They are the foundation of React Router's framework features and define automatic code-splitting, data loading, actions, revalidation, error boundaries, and more.
Loader async function example
Example of a route with a loader: createBrowserRouter([{ path: "/", loader: async () => { return { records: await getSomeRecords() }; }, Component: MyRoute }]);
Route loader provides data to components
Data is provided to route components from route loaders. The loader is an async function defined in the route configuration that returns data. This data is then available to the route component.