| 1 | ---
|
| 2 | title: Route Object
|
| 3 | order: 3
|
| 4 | ---
|
| 5 |
|
| 6 | # Route Object
|
| 7 |
|
| 8 | [MODES: data]
|
| 9 |
|
| 10 | ## Introduction
|
| 11 |
|
| 12 | The objects passed to `createBrowserRouter` are called Route Objects.
|
| 13 |
|
| 14 | ```tsx lines=[2-5]
|
| 15 | createBrowserRouter([
|
| 16 | {
|
| 17 | path: "/",
|
| 18 | Component: App,
|
| 19 | },
|
| 20 | ]);
|
| 21 | ```
|
| 22 |
|
| 23 | Route modules are the foundation of React Router's data features, they define:
|
| 24 |
|
| 25 | - data loading
|
| 26 | - actions
|
| 27 | - revalidation
|
| 28 | - error boundaries
|
| 29 | - and more
|
| 30 |
|
| 31 | This guide is a quick overview of every route object feature.
|
| 32 |
|
| 33 | ## Component
|
| 34 |
|
| 35 | The `Component` property in a route object defines the component that will render when the route matches.
|
| 36 |
|
| 37 | ```tsx lines=[4]
|
| 38 | createBrowserRouter([
|
| 39 | {
|
| 40 | path: "/",
|
| 41 | Component: MyRouteComponent,
|
| 42 | },
|
| 43 | ]);
|
| 44 |
|
| 45 | function MyRouteComponent() {
|
| 46 | return (
|
| 47 | <div>
|
| 48 | <h1>Look ma!</h1>
|
| 49 | <p>
|
| 50 | I'm still using React Router after like 10 years.
|
| 51 | </p>
|
| 52 | </div>
|
| 53 | );
|
| 54 | }
|
| 55 | ```
|
| 56 |
|
| 57 | ## `middleware`
|
| 58 |
|
| 59 | Route [middleware][middleware] runs sequentially before and after navigations. This gives you a singular place to do things like logging and authentication. The `next` function continues down the chain, and on the leaf route the `next` function executes the loaders/actions for the navigation.
|
| 60 |
|
| 61 | ```tsx
|
| 62 | createBrowserRouter([
|
| 63 | {
|
| 64 | path: "/",
|
| 65 | middleware: [loggingMiddleware],
|
| 66 | loader: rootLoader,
|
| 67 | Component: Root,
|
| 68 | children: [{
|
| 69 | path: 'auth',
|
| 70 | middleware: [authMiddleware],
|
| 71 | loader: authLoader,
|
| 72 | Component: Auth,
|
| 73 | children: [...]
|
| 74 | }]
|
| 75 | },
|
| 76 | ]);
|
| 77 |
|
| 78 | async function loggingMiddleware({ request }, next) {
|
| 79 | let url = new URL(request.url);
|
| 80 | console.log(`Starting navigation: ${url.pathname}${url.search}`);
|
| 81 | const start = performance.now();
|
| 82 | await next();
|
| 83 | const duration = performance.now() - start;
|
| 84 | console.log(`Navigation completed in ${duration}ms`);
|
| 85 | }
|
| 86 |
|
| 87 | const userContext = createContext<User>();
|
| 88 |
|
| 89 | async function authMiddleware ({ context }) {
|
| 90 | const userId = getUserId();
|
| 91 |
|
| 92 | if (!userId) {
|
| 93 | throw redirect("/login");
|
| 94 | }
|
| 95 |
|
| 96 | context.set(userContext, await getUserById(userId));
|
| 97 | };
|
| 98 | ```
|
| 99 |
|
| 100 | See also:
|
| 101 |
|
| 102 | - [Middleware][middleware]
|
| 103 |
|
| 104 | ## `loader`
|
| 105 |
|
| 106 | Route loaders provide data to route components before they are rendered.
|
| 107 |
|
| 108 | ```tsx
|
| 109 | import {
|
| 110 | useLoaderData,
|
| 111 | createBrowserRouter,
|
| 112 | } from "react-router";
|
| 113 |
|
| 114 | createBrowserRouter([
|
| 115 | {
|
| 116 | path: "/",
|
| 117 | loader: loader,
|
| 118 | Component: MyRoute,
|
| 119 | },
|
| 120 | ]);
|
| 121 |
|
| 122 | async function loader({ params }) {
|
| 123 | return { message: "Hello, world!" };
|
| 124 | }
|
| 125 |
|
| 126 | function MyRoute() {
|
| 127 | let data = useLoaderData();
|
| 128 | return <h1>{data.message}</h1>;
|
| 129 | }
|
| 130 | ```
|
| 131 |
|
| 132 | See also:
|
| 133 |
|
| 134 | - [`loader` params][loader-params]
|
| 135 |
|
| 136 | ## `action`
|
| 137 |
|
| 138 | Route actions allow server-side data mutations with automatic revalidation of all loader data on the page when called from `<Form>`, `useFetcher`, and `useSubmit`.
|
| 139 |
|
| 140 | ```tsx
|
| 141 | import {
|
| 142 | createBrowserRouter,
|
| 143 | useLoaderData,
|
| 144 | useActionData,
|
| 145 | Form,
|
| 146 | } from "react-router";
|
| 147 | import { TodoList } from "~/components/TodoList";
|
| 148 |
|
| 149 | createBrowserRouter([
|
| 150 | {
|
| 151 | path: "/items",
|
| 152 | action: action,
|
| 153 | loader: loader,
|
| 154 | Component: Items,
|
| 155 | },
|
| 156 | ]);
|
| 157 |
|
| 158 | async function action({ request }) {
|
| 159 | const data = await request.formData();
|
| 160 | const todo = await fakeDb.addItem({
|
| 161 | title: data.get("title"),
|
| 162 | });
|
| 163 | return { ok: true };
|
| 164 | }
|
| 165 |
|
| 166 | // this data will be revalidated after the action completes...
|
| 167 | async function loader() {
|
| 168 | const items = await fakeDb.getItems();
|
| 169 | return { items };
|
| 170 | }
|
| 171 |
|
| 172 | // ...so that the list here is updated automatically
|
| 173 | export default function Items() {
|
| 174 | let data = useLoaderData();
|
| 175 | return (
|
| 176 | <div>
|
| 177 | <List items={data.items} />
|
| 178 | <Form method="post" navigate={false}>
|
| 179 | <input type="text" name="title" />
|
| 180 | <button type="submit">Create Todo</button>
|
| 181 | </Form>
|
| 182 | </div>
|
| 183 | );
|
| 184 | }
|
| 185 | ```
|
| 186 |
|
| 187 | ## `shouldRevalidate`
|
| 188 |
|
| 189 | Loader data is automatically revalidated after certain events like navigations and form submissions.
|
| 190 |
|
| 191 | This hook enables you to opt in or out of the default revalidation behavior. The default behavior is nuanced to avoid calling loaders unnecessarily.
|
| 192 |
|
| 193 | A route loader is revalidated when:
|
| 194 |
|
| 195 | - its own route params change
|
| 196 | - any change to URL search params
|
| 197 | - after an action is called and returns a non-error status code
|
| 198 |
|
| 199 | By defining this function, you opt out of the default behavior completely and can manually control when loader data is revalidated for navigations and form submissions.
|
| 200 |
|
| 201 | ```tsx
|
| 202 | import type { ShouldRevalidateFunctionArgs } from "react-router";
|
| 203 |
|
| 204 | function shouldRevalidate(
|
| 205 | arg: ShouldRevalidateFunctionArgs,
|
| 206 | ) {
|
| 207 | return true; // false
|
| 208 | }
|
| 209 |
|
| 210 | createBrowserRouter([
|
| 211 | {
|
| 212 | path: "/",
|
| 213 | shouldRevalidate: shouldRevalidate,
|
| 214 | Component: MyRoute,
|
| 215 | },
|
| 216 | ]);
|
| 217 | ```
|
| 218 |
|
| 219 | [`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/react-router.ShouldRevalidateFunctionArgs.html)
|
| 220 |
|
| 221 | Please note the default behavior is different in [Framework Mode](../modes).
|
| 222 |
|
| 223 | ## `lazy`
|
| 224 |
|
| 225 | Most properties can be lazily imported to reduce the initial bundle size.
|
| 226 |
|
| 227 | ```tsx
|
| 228 | createBrowserRouter([
|
| 229 | {
|
| 230 | path: "/app",
|
| 231 | lazy: async () => {
|
| 232 | // load component and loader in parallel before rendering
|
| 233 | const [Component, loader] = await Promise.all([
|
| 234 | import("./app"),
|
| 235 | import("./app-loader"),
|
| 236 | ]);
|
| 237 | return { Component, loader };
|
| 238 | },
|
| 239 | },
|
| 240 | ]);
|
| 241 | ```
|
| 242 |
|
| 243 | ## `handle`
|
| 244 |
|
| 245 | Route handle allows apps to add anything to a route match in `useMatches` to create abstractions (like breadcrumbs, etc.).
|
| 246 |
|
| 247 | ```tsx
|
| 248 | createBrowserRouter([
|
| 249 | {
|
| 250 | path: "/app",
|
| 251 | handle: {
|
| 252 | breadcrumb: "App",
|
| 253 | },
|
| 254 | },
|
| 255 | ]);
|
| 256 | ```
|
| 257 |
|
| 258 | See also:
|
| 259 |
|
| 260 | - [`useMatches`][use-matches]
|
| 261 |
|
| 262 | ---
|
| 263 |
|
| 264 | Next: [Data Loading](./data-loading)
|
| 265 |
|
| 266 | [loader-params]: https://api.reactrouter.com/v7/interfaces/react-router.LoaderFunctionArgs
|
| 267 | [middleware]: ../../how-to/middleware
|
| 268 | [use-matches]: ../../api/hooks/useMatches
|