UNPKG

14.1 kB Markdown View Raw
1---
2title: Route Module
3order: 3
4---
5
6# Route Module
7
8[MODES: framework]
9
10## Introduction
11
12The files referenced in `routes.ts` are called Route Modules.
13
14```tsx filename=app/routes.ts
15route("teams/:teamId", "./team.tsx"),
16// route module ^^^^^^^^
17```
18
19Route modules are the foundation of React Router's framework features, they define:
20
21- automatic code-splitting
22- data loading
23- actions
24- revalidation
25- error boundaries
26- and more
27
28This guide is a quick overview of every route module feature. The rest of the getting started guides will cover these features in more detail.
29
30## Component (`default`)
31
32The `default` export in a route module defines the component that will render when the route matches.
33
34```tsx filename=app/routes/my-route.tsx
35export default function MyRouteComponent() {
36 return (
37 <div>
38 <h1>Look ma!</h1>
39 <p>
40 I'm still using React Router after like 10 years.
41 </p>
42 </div>
43 );
44}
45```
46
47### Props passed to the Component
48
49When the component is rendered, it is provided the props defined in `Route.ComponentProps` that React Router will automatically generate for you. These props include:
50
511. `loaderData`: The data returned from the `loader` function in this route module
522. `actionData`: The data returned from the `action` function in this route module
533. `params`: An object containing the route parameters (if any).
544. `matches`: An array of all the matches in the current route tree.
55
56You can use these props in place of hooks like `useLoaderData` or `useParams`. This may be preferable because they will be automatically typed correctly for the route.
57
58### Using props
59
60```tsx filename=app/routes/my-route-with-default-params.tsx
61import type { Route } from "./+types/route-name";
62
63export default function MyRouteComponent({
64 loaderData,
65 actionData,
66 params,
67 matches,
68}: Route.ComponentProps) {
69 return (
70 <div>
71 <h1>Welcome to My Route with Props!</h1>
72 <p>Loader Data: {JSON.stringify(loaderData)}</p>
73 <p>Action Data: {JSON.stringify(actionData)}</p>
74 <p>Route Parameters: {JSON.stringify(params)}</p>
75 <p>Matched Routes: {JSON.stringify(matches)}</p>
76 </div>
77 );
78}
79```
80
81## `middleware`
82
83Route [middleware][middleware] runs sequentially on the server before and after document and
84data requests. This gives you a singular place to do things like logging,
85authentication, and post-processing of responses. The `next` function continues down the chain, and on the leaf route the `next` function executes the loaders/actions for the navigation.
86
87Here's an example middleware to log requests on the server:
88
89```tsx filename=root.tsx
90async function loggingMiddleware(
91 { request, context },
92 next,
93) {
94 console.log(
95 `${new Date().toISOString()} ${request.method} ${request.url}`,
96 );
97 const start = performance.now();
98 const response = await next();
99 const duration = performance.now() - start;
100 console.log(
101 `${new Date().toISOString()} Response ${response.status} (${duration}ms)`,
102 );
103 return response;
104}
105
106export const middleware = [loggingMiddleware];
107```
108
109Here's an example middleware to check for logged in users and set the user in
110`context` you can then access from loaders:
111
112```tsx filename=routes/_auth.tsx
113async function authMiddleware({ request, context }) {
114 const session = await getSession(request);
115 const userId = session.get("userId");
116
117 if (!userId) {
118 throw redirect("/login");
119 }
120
121 const user = await getUserById(userId);
122 context.set(userContext, user);
123}
124
125export const middleware = [authMiddleware];
126```
127
128<docs-warning>Please make sure you understand [when middleware runs][when-middleware-runs] to make sure your application will behave the way you intend when adding middleware to your routes.</docs-warning>
129
130See also:
131
132- [`middleware` params][middleware-params]
133- [Middleware][middleware]
134
135## `clientMiddleware`
136
137This is the client-side equivalent of `middleware` and runs in the browser during client navigations. The only difference from server middleware is that client middleware doesn't return Responses because they're not wrapping an HTTP request on the server.
138
139Here's an example middleware to log requests on the client:
140
141```tsx filename=root.tsx
142async function loggingMiddleware(
143 { request, context },
144 next,
145) {
146 console.log(
147 `${new Date().toISOString()} ${request.method} ${request.url}`,
148 );
149 const start = performance.now();
150 await next(); // 👈 No Response returned
151 const duration = performance.now() - start;
152 console.log(
153 `${new Date().toISOString()} (${duration}ms)`,
154 );
155 // ✅ No need to return anything
156}
157
158export const clientMiddleware = [loggingMiddleware];
159```
160
161See also:
162
163- [Middleware][middleware]
164- [Client Data][client-data]
165
166## `loader`
167
168Route loaders provide data to route components before they are rendered. They are only called on the server when server rendering or during the build with pre-rendering.
169
170```tsx
171export async function loader() {
172 return { message: "Hello, world!" };
173}
174
175export default function MyRoute({ loaderData }) {
176 return <h1>{loaderData.message}</h1>;
177}
178```
179
180See also:
181
182- [`loader` params][loader-params]
183
184## `clientLoader`
185
186Called only in the browser, route client loaders provide data to route components in addition to, or in place of, route loaders.
187
188```tsx
189export async function clientLoader({ serverLoader }) {
190 // call the server loader
191 const serverData = await serverLoader();
192 // And/or fetch data on the client
193 const data = getDataFromClient();
194 // Return the data to expose through useLoaderData()
195 return data;
196}
197```
198
199Client loaders can participate in initial page load hydration of server rendered pages by setting the `hydrate` property on the function:
200
201```tsx
202export async function clientLoader() {
203 // ...
204}
205clientLoader.hydrate = true as const;
206```
207
208<docs-info>
209
210By using `as const`, TypeScript will infer that the type for `clientLoader.hydrate` is `true` instead of `boolean`.
211That way, React Router can derive types for `loaderData` based on the value of `clientLoader.hydrate`.
212
213</docs-info>
214
215See also:
216
217- [`clientLoader` params][client-loader-params]
218- [Client Data][client-data]
219
220## `action`
221
222Route actions allow server-side data mutations with automatic revalidation of all loader data on the page when called from `<Form>`, `useFetcher`, and `useSubmit`.
223
224```tsx
225// route("/list", "./list.tsx")
226import { Form } from "react-router";
227import { TodoList } from "~/components/TodoList";
228
229// this data will be loaded after the action completes...
230export async function loader() {
231 const items = await fakeDb.getItems();
232 return { items };
233}
234
235// ...so that the list here is updated automatically
236export default function Items({ loaderData }) {
237 return (
238 <div>
239 <List items={loaderData.items} />
240 <Form method="post" navigate={false} action="/list">
241 <input type="text" name="title" />
242 <button type="submit">Create Todo</button>
243 </Form>
244 </div>
245 );
246}
247
248export async function action({ request }) {
249 const data = await request.formData();
250 const todo = await fakeDb.addItem({
251 title: data.get("title"),
252 });
253 return { ok: true };
254}
255```
256
257See also:
258
259- [`action` params][action-params]
260
261## `clientAction`
262
263Like route actions but only called in the browser.
264
265```tsx
266export async function clientAction({ serverAction }) {
267 fakeInvalidateClientSideCache();
268 // can still call the server action if needed
269 const data = await serverAction();
270 return data;
271}
272```
273
274See also:
275
276- [`clientAction` params][client-action-params]
277- [Client Data][client-data]
278
279## `ErrorBoundary`
280
281When other route module APIs throw, the route module `ErrorBoundary` will render instead of the route component.
282
283```tsx
284import {
285 isRouteErrorResponse,
286 useRouteError,
287} from "react-router";
288
289export function ErrorBoundary() {
290 const error = useRouteError();
291
292 if (isRouteErrorResponse(error)) {
293 return (
294 <div>
295 <h1>
296 {error.status} {error.statusText}
297 </h1>
298 <p>{error.data}</p>
299 </div>
300 );
301 } else if (error instanceof Error) {
302 return (
303 <div>
304 <h1>Error</h1>
305 <p>{error.message}</p>
306 <p>The stack trace is:</p>
307 <pre>{error.stack}</pre>
308 </div>
309 );
310 } else {
311 return <h1>Unknown Error</h1>;
312 }
313}
314```
315
316See also:
317
318- [`useRouteError`][use-route-error]
319- [`isRouteErrorResponse`][is-route-error-response]
320
321## `HydrateFallback`
322
323On initial page load, the route component renders only after the client loader is finished. If exported, a `HydrateFallback` can render immediately in place of the route component.
324
325```tsx filename=routes/client-only-route.tsx
326export async function clientLoader() {
327 const data = await fakeLoadLocalGameData();
328 return data;
329}
330
331export function HydrateFallback() {
332 return <p>Loading Game...</p>;
333}
334
335export default function Component({ loaderData }) {
336 return <Game data={loaderData} />;
337}
338```
339
340## `headers`
341
342The route `headers` function defines the HTTP headers to be sent with the response when server rendering.
343
344```tsx
345export function headers() {
346 return {
347 "X-Stretchy-Pants": "its for fun",
348 "Cache-Control": "max-age=300, s-maxage=3600",
349 };
350}
351```
352
353See also:
354
355- [`Headers`][headers]
356
357## `handle`
358
359Route handle allows apps to add anything to a route match in `useMatches` to create abstractions (like breadcrumbs, etc.).
360
361```tsx
362export const handle = {
363 its: "all yours",
364};
365```
366
367See also:
368
369- [`useMatches`][use-matches]
370
371## `links`
372
373Route links define [`<link>` element][link-element]s to be rendered in the document `<head>`.
374
375```tsx
376export function links() {
377 return [
378 {
379 rel: "icon",
380 href: "/favicon.png",
381 type: "image/png",
382 },
383 {
384 rel: "stylesheet",
385 href: "https://example.com/some/styles.css",
386 },
387 {
388 rel: "preload",
389 href: "/images/banner.jpg",
390 as: "image",
391 },
392 ];
393}
394```
395
396All routes links will be aggregated and rendered through the `<Links />` component, usually rendered in your app root:
397
398```tsx
399import { Links } from "react-router";
400
401export default function Root() {
402 return (
403 <html>
404 <head>
405 <Links />
406 </head>
407
408 <body />
409 </html>
410 );
411}
412```
413
414See also:
415
416- [Styling][styling]
417
418## `meta`
419
420Route meta defines [meta tags][meta-element] to be rendered in the `<Meta />` component, usually placed in the `<head>`.
421
422<docs-warning>
423
424Since React 19, [using the built-in `<meta>` element](https://react.dev/reference/react-dom/components/meta) is recommended over the use of the route module's `meta` export.
425
426Here is an example of how to use it and the `<title>` element:
427
428```tsx
429export default function MyRoute() {
430 return (
431 <div>
432 <title>Very cool app</title>
433 <meta property="og:title" content="Very cool app" />
434 <meta
435 name="description"
436 content="This app is the best"
437 />
438 {/* The rest of your route content... */}
439 </div>
440 );
441}
442```
443
444</docs-warning>
445
446```tsx filename=app/product.tsx
447export function meta() {
448 return [
449 { title: "Very cool app" },
450 {
451 property: "og:title",
452 content: "Very cool app",
453 },
454 {
455 name: "description",
456 content: "This app is the best",
457 },
458 ];
459}
460```
461
462```tsx filename=app/root.tsx
463import { Meta } from "react-router";
464
465export default function Root() {
466 return (
467 <html>
468 <head>
469 <Meta />
470 </head>
471
472 <body />
473 </html>
474 );
475}
476```
477
478The meta of the last matching route is used, allowing you to override parent routes' meta. It's important to note that the entire meta descriptor array is replaced, not merged. This gives you the flexibility to build your own meta composition logic across pages at different levels.
479
480**See also**
481
482- [`meta` params][meta-params]
483- [`meta` function return types][meta-function]
484
485## `shouldRevalidate`
486
487In framework mode with SSR, route loaders are automatically revalidated after all navigations and form submissions (this is different from [Data Mode][data-mode-should-revalidate]). This enables middleware and loaders to share a request context and optimize in different ways than they would in Data Mode.
488
489Defining this function allows you to opt out of revalidation for a route loader for navigations and form submissions.
490
491```tsx
492import type { ShouldRevalidateFunctionArgs } from "react-router";
493
494export function shouldRevalidate(
495 arg: ShouldRevalidateFunctionArgs,
496) {
497 return true;
498}
499```
500
501When using [SPA Mode][spa-mode], there are no server loaders to call on navigations, so `shouldRevalidate` behaves the same as it does in [Data Mode][data-mode-should-revalidate].
502
503[`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/react-router.ShouldRevalidateFunctionArgs.html)
504
505---
506
507Next: [Rendering Strategies](./rendering)
508
509[middleware-params]: https://api.reactrouter.com/v7/types/react-router.MiddlewareFunction.html
510[middleware]: ../../how-to/middleware
511[when-middleware-runs]: ../../how-to/middleware#when-middleware-runs
512[loader-params]: https://api.reactrouter.com/v7/interfaces/react-router.LoaderFunctionArgs
513[client-loader-params]: https://api.reactrouter.com/v7/types/react-router.ClientLoaderFunctionArgs
514[action-params]: https://api.reactrouter.com/v7/interfaces/react-router.ActionFunctionArgs
515[client-action-params]: https://api.reactrouter.com/v7/types/react-router.ClientActionFunctionArgs
516[use-route-error]: ../../api/hooks/useRouteError
517[is-route-error-response]: ../../api/utils/isRouteErrorResponse
518[headers]: https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
519[use-matches]: ../../api/hooks/useMatches
520[link-element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link
521[meta-element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
522[meta-params]: https://api.reactrouter.com/v7/interfaces/react-router.MetaArgs
523[meta-function]: https://api.reactrouter.com/v7/types/react-router.MetaDescriptor.html
524[data-mode-should-revalidate]: ../data/route-object#shouldrevalidate
525[spa-mode]: ../../how-to/spa
526[client-data]: ../../how-to/client-data
527[styling]: ../../explanation/styling