react-router 7.15.1 → 7.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -2
- package/dist/development/{browser-3AnU12UI.d.mts → browser-CGcs-0pD.d.mts} +2 -2
- package/dist/development/{browser-BOdXz9dK.d.ts → browser-D3uq9sI1.d.ts} +2 -2
- package/dist/development/{chunk-4N6VE7H7.mjs → chunk-6CSD65Y2.mjs} +12 -11
- package/dist/development/{chunk-RJYABSBD.mjs → chunk-ASILSGTR.mjs} +6 -6
- package/dist/development/{chunk-D6LUOGOQ.js → chunk-KFNXW4AL.js} +10 -10
- package/dist/{production/chunk-Y6IFXO7V.js → development/chunk-PBLBZ3QU.js} +7 -7
- package/dist/development/{chunk-66UKHEGQ.js → chunk-PULC7NLK.js} +100 -99
- package/dist/{production/context-ByvtofY2.d.mts → development/context-CmHpk1Ws.d.mts} +3 -3
- package/dist/development/{data-BqZ2x964.d.ts → data-D4xhSy90.d.ts} +1 -1
- package/dist/{production/data-BVUf681J.d.mts → development/data-U8FS-wNn.d.mts} +1 -1
- package/dist/development/dom-export.d.mts +4 -4
- package/dist/development/dom-export.d.ts +4 -4
- package/dist/development/dom-export.js +30 -30
- package/dist/development/dom-export.mjs +5 -5
- package/dist/{production/index-react-server-client-BS5F89FR.d.ts → development/index-react-server-client-CwU9bE5R.d.ts} +4 -4
- package/dist/{production/index-react-server-client-DY04-103.d.mts → development/index-react-server-client-DPrDrCew.d.mts} +3 -3
- package/dist/development/index-react-server-client.d.mts +3 -3
- package/dist/development/index-react-server-client.d.ts +3 -3
- package/dist/development/index-react-server-client.js +4 -4
- package/dist/development/index-react-server-client.mjs +2 -2
- package/dist/development/index-react-server.d.mts +1 -1
- package/dist/development/index-react-server.d.ts +1 -1
- package/dist/development/index-react-server.js +1 -1
- package/dist/development/index-react-server.mjs +1 -1
- package/dist/development/index.d.mts +18 -12
- package/dist/development/index.d.ts +18 -12
- package/dist/development/index.js +89 -89
- package/dist/development/index.mjs +3 -3
- package/dist/{production/instrumentation-cRWWLfsU.d.ts → development/instrumentation-1q4YhLGP.d.ts} +2 -2
- package/dist/development/lib/types/internal.d.mts +2 -2
- package/dist/development/lib/types/internal.d.ts +2 -2
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/lib/types/internal.mjs +1 -1
- package/dist/development/{register-Bsscfj79.d.ts → register-CNAx3TXj.d.ts} +1 -1
- package/dist/development/{register-Df8okEea.d.mts → register-CqK96Zfk.d.mts} +1 -1
- package/dist/production/{browser-3AnU12UI.d.mts → browser-CGcs-0pD.d.mts} +2 -2
- package/dist/production/{browser-BOdXz9dK.d.ts → browser-D3uq9sI1.d.ts} +2 -2
- package/dist/production/{chunk-6S4627ZB.mjs → chunk-5TQZEVD5.mjs} +6 -6
- package/dist/{development/chunk-4YRVXM2U.js → production/chunk-CTIXC7EV.js} +7 -7
- package/dist/production/{chunk-PNZCCTKT.js → chunk-EN242BO4.js} +100 -99
- package/dist/production/{chunk-JAKZPQZC.mjs → chunk-OSYEOCBT.mjs} +12 -11
- package/dist/production/{chunk-HUBUW7R3.js → chunk-RTRY3JFT.js} +10 -10
- package/dist/{development/context-ByvtofY2.d.mts → production/context-CmHpk1Ws.d.mts} +3 -3
- package/dist/production/{data-BqZ2x964.d.ts → data-D4xhSy90.d.ts} +1 -1
- package/dist/{development/data-BVUf681J.d.mts → production/data-U8FS-wNn.d.mts} +1 -1
- package/dist/production/dom-export.d.mts +4 -4
- package/dist/production/dom-export.d.ts +4 -4
- package/dist/production/dom-export.js +30 -30
- package/dist/production/dom-export.mjs +5 -5
- package/dist/{development/index-react-server-client-BS5F89FR.d.ts → production/index-react-server-client-CwU9bE5R.d.ts} +4 -4
- package/dist/{development/index-react-server-client-DY04-103.d.mts → production/index-react-server-client-DPrDrCew.d.mts} +3 -3
- package/dist/production/index-react-server-client.d.mts +3 -3
- package/dist/production/index-react-server-client.d.ts +3 -3
- package/dist/production/index-react-server-client.js +4 -4
- package/dist/production/index-react-server-client.mjs +2 -2
- package/dist/production/index-react-server.d.mts +1 -1
- package/dist/production/index-react-server.d.ts +1 -1
- package/dist/production/index-react-server.js +1 -1
- package/dist/production/index-react-server.mjs +1 -1
- package/dist/production/index.d.mts +18 -12
- package/dist/production/index.d.ts +18 -12
- package/dist/production/index.js +89 -89
- package/dist/production/index.mjs +3 -3
- package/dist/{development/instrumentation-cRWWLfsU.d.ts → production/instrumentation-1q4YhLGP.d.ts} +2 -2
- package/dist/production/lib/types/internal.d.mts +2 -2
- package/dist/production/lib/types/internal.d.ts +2 -2
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/lib/types/internal.mjs +1 -1
- package/dist/production/{register-Bsscfj79.d.ts → register-CNAx3TXj.d.ts} +1 -1
- package/dist/production/{register-Df8okEea.d.mts → register-CqK96Zfk.d.mts} +1 -1
- package/docs/explanation/backend-for-frontend.md +50 -0
- package/docs/explanation/code-splitting.md +61 -0
- package/docs/explanation/concurrency.md +135 -0
- package/docs/explanation/form-vs-fetcher.md +292 -0
- package/docs/explanation/hot-module-replacement.md +137 -0
- package/docs/explanation/hydration.md +14 -0
- package/docs/explanation/index-query-param.md +86 -0
- package/docs/explanation/index.md +4 -0
- package/docs/explanation/lazy-route-discovery.md +78 -0
- package/docs/explanation/location.md +6 -0
- package/docs/explanation/progressive-enhancement.md +150 -0
- package/docs/explanation/race-conditions.md +88 -0
- package/docs/explanation/react-transitions.md +160 -0
- package/docs/explanation/route-matching.md +7 -0
- package/docs/explanation/server-client-execution.md +4 -0
- package/docs/explanation/sessions-and-cookies.md +465 -0
- package/docs/explanation/special-files.md +16 -0
- package/docs/explanation/state-management.md +524 -0
- package/docs/explanation/styling.md +87 -0
- package/docs/explanation/type-safety.md +82 -0
- package/docs/how-to/accessibility.md +44 -0
- package/docs/how-to/client-data.md +199 -0
- package/docs/how-to/data-strategy.md +317 -0
- package/docs/how-to/error-boundary.md +231 -0
- package/docs/how-to/error-reporting.md +142 -0
- package/docs/how-to/fetchers.md +307 -0
- package/docs/how-to/file-route-conventions.md +410 -0
- package/docs/how-to/file-uploads.md +217 -0
- package/docs/how-to/form-validation.md +120 -0
- package/docs/how-to/headers.md +164 -0
- package/docs/how-to/index.md +4 -0
- package/docs/how-to/instrumentation.md +556 -0
- package/docs/how-to/meta.md +40 -0
- package/docs/how-to/middleware.md +763 -0
- package/docs/how-to/navigation-blocking.md +233 -0
- package/docs/how-to/optimize-revalidation.md +12 -0
- package/docs/how-to/pre-rendering.md +225 -0
- package/docs/how-to/presets.md +103 -0
- package/docs/how-to/react-server-components.md +899 -0
- package/docs/how-to/resource-routes.md +126 -0
- package/docs/how-to/route-module-type-safety.md +100 -0
- package/docs/how-to/search-params.md +4 -0
- package/docs/how-to/security.md +30 -0
- package/docs/how-to/server-bundles.md +66 -0
- package/docs/how-to/spa.md +120 -0
- package/docs/how-to/status.md +63 -0
- package/docs/how-to/suspense.md +132 -0
- package/docs/how-to/using-handle.md +117 -0
- package/docs/how-to/view-transitions.md +237 -0
- package/docs/how-to/webhook.md +50 -0
- package/docs/index.md +39 -0
- package/docs/start/data/actions.md +138 -0
- package/docs/start/data/custom.md +198 -0
- package/docs/start/data/data-loading.md +44 -0
- package/docs/start/data/index.md +4 -0
- package/docs/start/data/installation.md +52 -0
- package/docs/start/data/navigating.md +12 -0
- package/docs/start/data/pending-ui.md +12 -0
- package/docs/start/data/route-object.md +268 -0
- package/docs/start/data/routing.md +281 -0
- package/docs/start/data/testing.md +8 -0
- package/docs/start/declarative/index.md +4 -0
- package/docs/start/declarative/installation.md +43 -0
- package/docs/start/declarative/navigating.md +133 -0
- package/docs/start/declarative/routing.md +237 -0
- package/docs/start/declarative/url-values.md +65 -0
- package/docs/start/framework/actions.md +174 -0
- package/docs/start/framework/data-loading.md +201 -0
- package/docs/start/framework/deploying.md +96 -0
- package/docs/start/framework/index.md +4 -0
- package/docs/start/framework/installation.md +41 -0
- package/docs/start/framework/navigating.md +182 -0
- package/docs/start/framework/pending-ui.md +142 -0
- package/docs/start/framework/rendering.md +59 -0
- package/docs/start/framework/route-module.md +527 -0
- package/docs/start/framework/routing.md +362 -0
- package/docs/start/framework/testing.md +133 -0
- package/docs/start/index.md +4 -0
- package/docs/start/modes.md +201 -0
- package/docs/upgrading/component-routes.md +363 -0
- package/docs/upgrading/future.md +280 -0
- package/docs/upgrading/index.md +4 -0
- package/docs/upgrading/remix.md +403 -0
- package/docs/upgrading/router-provider.md +442 -0
- package/docs/upgrading/v6.md +382 -0
- package/package.json +2 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Custom Framework
|
|
3
|
+
order: 8
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Custom Framework
|
|
7
|
+
|
|
8
|
+
[MODES: data]
|
|
9
|
+
|
|
10
|
+
## Introduction
|
|
11
|
+
|
|
12
|
+
Instead of using `@react-router/dev`, you can integrate React Router's framework features (like loaders, actions, fetchers, etc.) into your own bundler and server abstractions with Data Mode.
|
|
13
|
+
|
|
14
|
+
## Client Rendering
|
|
15
|
+
|
|
16
|
+
### 1. Create a Router
|
|
17
|
+
|
|
18
|
+
The browser runtime API that enables route module APIs (loaders, actions, etc.) is `createBrowserRouter`.
|
|
19
|
+
|
|
20
|
+
It takes an array of route objects that support loaders, actions, error boundaries and more. The React Router Vite plugin creates one of these from `routes.ts`, but you can create one manually (or with an abstraction) and use your own bundler.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { createBrowserRouter } from "react-router";
|
|
24
|
+
|
|
25
|
+
let router = createBrowserRouter([
|
|
26
|
+
{
|
|
27
|
+
path: "/",
|
|
28
|
+
Component: Root,
|
|
29
|
+
children: [
|
|
30
|
+
{
|
|
31
|
+
path: "shows/:showId",
|
|
32
|
+
Component: Show,
|
|
33
|
+
loader: ({ request, params }) =>
|
|
34
|
+
fetch(`/api/show/${params.showId}.json`, {
|
|
35
|
+
signal: request.signal,
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Render the Router
|
|
44
|
+
|
|
45
|
+
To render the router in the browser, use `<RouterProvider>`.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import {
|
|
49
|
+
createBrowserRouter,
|
|
50
|
+
RouterProvider,
|
|
51
|
+
} from "react-router";
|
|
52
|
+
import { createRoot } from "react-dom/client";
|
|
53
|
+
|
|
54
|
+
createRoot(document.getElementById("root")).render(
|
|
55
|
+
<RouterProvider router={router} />,
|
|
56
|
+
);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Lazy Loading
|
|
60
|
+
|
|
61
|
+
Routes can take most of their definition lazily with the `lazy` property.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
createBrowserRouter([
|
|
65
|
+
{
|
|
66
|
+
path: "/show/:showId",
|
|
67
|
+
lazy: {
|
|
68
|
+
loader: async () =>
|
|
69
|
+
(await import("./show.loader.js")).loader,
|
|
70
|
+
action: async () =>
|
|
71
|
+
(await import("./show.action.js")).action,
|
|
72
|
+
Component: async () =>
|
|
73
|
+
(await import("./show.component.js")).Component,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Server Rendering
|
|
80
|
+
|
|
81
|
+
To server render a custom setup, there are a few server APIs available for rendering and data loading.
|
|
82
|
+
|
|
83
|
+
This guide simply gives you some ideas about how it works. For deeper understanding, please see the [Custom Framework Example Repo](https://github.com/remix-run/custom-react-router-framework-example)
|
|
84
|
+
|
|
85
|
+
### 1. Define Your Routes
|
|
86
|
+
|
|
87
|
+
Routes are the same kinds of objects on the server as the client.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
export default [
|
|
91
|
+
{
|
|
92
|
+
path: "/",
|
|
93
|
+
Component: Root,
|
|
94
|
+
children: [
|
|
95
|
+
{
|
|
96
|
+
path: "shows/:showId",
|
|
97
|
+
Component: Show,
|
|
98
|
+
loader: ({ params }) => {
|
|
99
|
+
return db.loadShow(params.id);
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 2. Create a static handler
|
|
108
|
+
|
|
109
|
+
Turn your routes into a request handler with `createStaticHandler`:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { createStaticHandler } from "react-router";
|
|
113
|
+
import routes from "./some-routes";
|
|
114
|
+
|
|
115
|
+
let { query, dataRoutes } = createStaticHandler(routes);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 3. Get Routing Context and Render
|
|
119
|
+
|
|
120
|
+
React Router works with web fetch [Requests](https://developer.mozilla.org/en-US/docs/Web/API/Request), so if your server doesn't, you'll need to adapt whatever objects it uses to a web fetch `Request` object.
|
|
121
|
+
|
|
122
|
+
This step assumes your server receives `Request` objects.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { renderToString } from "react-dom/server";
|
|
126
|
+
import {
|
|
127
|
+
createStaticHandler,
|
|
128
|
+
createStaticRouter,
|
|
129
|
+
StaticRouterProvider,
|
|
130
|
+
} from "react-router";
|
|
131
|
+
|
|
132
|
+
import routes from "./some-routes.js";
|
|
133
|
+
|
|
134
|
+
let { query, dataRoutes } = createStaticHandler(routes);
|
|
135
|
+
|
|
136
|
+
export async function handler(request: Request) {
|
|
137
|
+
// 1. run actions/loaders to get the routing context with `query`
|
|
138
|
+
let context = await query(request);
|
|
139
|
+
|
|
140
|
+
// If `query` returns a Response, send it raw (a route probably a redirected)
|
|
141
|
+
if (context instanceof Response) {
|
|
142
|
+
return context;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 2. Create a static router for SSR
|
|
146
|
+
let router = createStaticRouter(dataRoutes, context);
|
|
147
|
+
|
|
148
|
+
// 3. Render everything with StaticRouterProvider
|
|
149
|
+
let html = renderToString(
|
|
150
|
+
<StaticRouterProvider
|
|
151
|
+
router={router}
|
|
152
|
+
context={context}
|
|
153
|
+
/>,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// Setup headers from action and loaders from deepest match
|
|
157
|
+
let leaf = context.matches[context.matches.length - 1];
|
|
158
|
+
let actionHeaders = context.actionHeaders[leaf.route.id];
|
|
159
|
+
let loaderHeaders = context.loaderHeaders[leaf.route.id];
|
|
160
|
+
let headers = new Headers(actionHeaders);
|
|
161
|
+
if (loaderHeaders) {
|
|
162
|
+
for (let [key, value] of loaderHeaders.entries()) {
|
|
163
|
+
headers.append(key, value);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
168
|
+
|
|
169
|
+
// 4. send a response
|
|
170
|
+
return new Response(`<!DOCTYPE html>${html}`, {
|
|
171
|
+
status: context.statusCode,
|
|
172
|
+
headers,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 4. Hydrate in the browser
|
|
178
|
+
|
|
179
|
+
Hydration data is embedded onto `window.__staticRouterHydrationData`, use that to initialize your client side router and render a `<RouterProvider>`.
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import { StrictMode } from "react";
|
|
183
|
+
import { hydrateRoot } from "react-dom/client";
|
|
184
|
+
import { RouterProvider } from "react-router/dom";
|
|
185
|
+
import routes from "./app/routes.js";
|
|
186
|
+
import { createBrowserRouter } from "react-router";
|
|
187
|
+
|
|
188
|
+
let router = createBrowserRouter(routes, {
|
|
189
|
+
hydrationData: window.__staticRouterHydrationData,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
hydrateRoot(
|
|
193
|
+
document,
|
|
194
|
+
<StrictMode>
|
|
195
|
+
<RouterProvider router={router} />
|
|
196
|
+
</StrictMode>,
|
|
197
|
+
);
|
|
198
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Data Loading
|
|
3
|
+
order: 4
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Data Loading
|
|
7
|
+
|
|
8
|
+
[MODES: data]
|
|
9
|
+
|
|
10
|
+
## Providing Data
|
|
11
|
+
|
|
12
|
+
Data is provided to route components from route loaders:
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
createBrowserRouter([
|
|
16
|
+
{
|
|
17
|
+
path: "/",
|
|
18
|
+
loader: async () => {
|
|
19
|
+
// return data from here
|
|
20
|
+
return { records: await getSomeRecords() };
|
|
21
|
+
},
|
|
22
|
+
Component: MyRoute,
|
|
23
|
+
},
|
|
24
|
+
]);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Accessing Data
|
|
28
|
+
|
|
29
|
+
The data is available in route components with `useLoaderData`.
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { useLoaderData } from "react-router";
|
|
33
|
+
|
|
34
|
+
function MyRoute() {
|
|
35
|
+
const { records } = useLoaderData();
|
|
36
|
+
return <div>{records.length}</div>;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
As the user navigates between routes, the loaders are called before the route component is rendered.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
Next: [Actions](./actions)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Installation
|
|
3
|
+
order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Installation
|
|
7
|
+
|
|
8
|
+
[MODES: data]
|
|
9
|
+
|
|
10
|
+
## Bootstrap with a Bundler Template
|
|
11
|
+
|
|
12
|
+
You can start with a React template from Vite and choose "React", otherwise bootstrap your application however you prefer (Parcel, Webpack, etc).
|
|
13
|
+
|
|
14
|
+
```shellscript nonumber
|
|
15
|
+
npx create-vite@latest
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Install React Router
|
|
19
|
+
|
|
20
|
+
Next install React Router from npm:
|
|
21
|
+
|
|
22
|
+
```shellscript nonumber
|
|
23
|
+
npm i react-router
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Create a Router and Render
|
|
27
|
+
|
|
28
|
+
Create a router and pass it to `RouterProvider`:
|
|
29
|
+
|
|
30
|
+
```tsx lines=[3-4,6-11,16]
|
|
31
|
+
import React from "react";
|
|
32
|
+
import ReactDOM from "react-dom/client";
|
|
33
|
+
import { createBrowserRouter } from "react-router";
|
|
34
|
+
import { RouterProvider } from "react-router/dom";
|
|
35
|
+
|
|
36
|
+
const router = createBrowserRouter([
|
|
37
|
+
{
|
|
38
|
+
path: "/",
|
|
39
|
+
element: <div>Hello World</div>,
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const root = document.getElementById("root");
|
|
44
|
+
|
|
45
|
+
ReactDOM.createRoot(root).render(
|
|
46
|
+
<RouterProvider router={router} />,
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
Next: [Routing](./routing)
|
|
@@ -0,0 +1,268 @@
|
|
|
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
|