react-router 7.16.0 → 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.
Files changed (137) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/dist/development/{browser-nIQ4Nsyi.d.mts → browser-CGcs-0pD.d.mts} +1 -1
  3. package/dist/development/{chunk-QUQL4437.mjs → chunk-6CSD65Y2.mjs} +2 -2
  4. package/dist/{production/chunk-NALGHHKE.mjs → development/chunk-ASILSGTR.mjs} +2 -2
  5. package/dist/development/{chunk-SRID2YZ2.js → chunk-KFNXW4AL.js} +1 -1
  6. package/dist/development/{chunk-XEJDWL2B.js → chunk-PBLBZ3QU.js} +7 -7
  7. package/dist/{production/chunk-SKEDDLRM.js → development/chunk-PULC7NLK.js} +99 -99
  8. package/dist/development/{context-m8rizgnE.d.mts → context-CmHpk1Ws.d.mts} +1 -1
  9. package/dist/development/dom-export.d.mts +3 -3
  10. package/dist/development/dom-export.d.ts +1 -1
  11. package/dist/development/dom-export.js +28 -28
  12. package/dist/development/dom-export.mjs +3 -3
  13. package/dist/development/{index-react-server-client-BLiUx67a.d.ts → index-react-server-client-CwU9bE5R.d.ts} +1 -1
  14. package/dist/development/{index-react-server-client-CdKROblb.d.mts → index-react-server-client-DPrDrCew.d.mts} +1 -1
  15. package/dist/development/index-react-server-client.d.mts +2 -2
  16. package/dist/development/index-react-server-client.d.ts +1 -1
  17. package/dist/development/index-react-server-client.js +4 -4
  18. package/dist/development/index-react-server-client.mjs +2 -2
  19. package/dist/development/index-react-server.js +1 -1
  20. package/dist/development/index-react-server.mjs +1 -1
  21. package/dist/development/index.d.mts +6 -6
  22. package/dist/development/index.d.ts +2 -2
  23. package/dist/development/index.js +85 -85
  24. package/dist/development/index.mjs +3 -3
  25. package/dist/development/lib/types/internal.js +1 -1
  26. package/dist/development/lib/types/internal.mjs +1 -1
  27. package/dist/production/{browser-nIQ4Nsyi.d.mts → browser-CGcs-0pD.d.mts} +1 -1
  28. package/dist/{development/chunk-S54KXAEJ.mjs → production/chunk-5TQZEVD5.mjs} +2 -2
  29. package/dist/production/{chunk-EAQNHM3N.js → chunk-CTIXC7EV.js} +7 -7
  30. package/dist/{development/chunk-IBI7OMNB.js → production/chunk-EN242BO4.js} +99 -99
  31. package/dist/production/{chunk-Q65P7S7Y.mjs → chunk-OSYEOCBT.mjs} +2 -2
  32. package/dist/production/{chunk-Y7DNFQZP.js → chunk-RTRY3JFT.js} +1 -1
  33. package/dist/production/{context-m8rizgnE.d.mts → context-CmHpk1Ws.d.mts} +1 -1
  34. package/dist/production/dom-export.d.mts +3 -3
  35. package/dist/production/dom-export.d.ts +1 -1
  36. package/dist/production/dom-export.js +28 -28
  37. package/dist/production/dom-export.mjs +3 -3
  38. package/dist/production/{index-react-server-client-BLiUx67a.d.ts → index-react-server-client-CwU9bE5R.d.ts} +1 -1
  39. package/dist/production/{index-react-server-client-CdKROblb.d.mts → index-react-server-client-DPrDrCew.d.mts} +1 -1
  40. package/dist/production/index-react-server-client.d.mts +2 -2
  41. package/dist/production/index-react-server-client.d.ts +1 -1
  42. package/dist/production/index-react-server-client.js +4 -4
  43. package/dist/production/index-react-server-client.mjs +2 -2
  44. package/dist/production/index-react-server.js +1 -1
  45. package/dist/production/index-react-server.mjs +1 -1
  46. package/dist/production/index.d.mts +6 -6
  47. package/dist/production/index.d.ts +2 -2
  48. package/dist/production/index.js +85 -85
  49. package/dist/production/index.mjs +3 -3
  50. package/dist/production/lib/types/internal.js +1 -1
  51. package/dist/production/lib/types/internal.mjs +1 -1
  52. package/docs/explanation/backend-for-frontend.md +50 -0
  53. package/docs/explanation/code-splitting.md +61 -0
  54. package/docs/explanation/concurrency.md +135 -0
  55. package/docs/explanation/form-vs-fetcher.md +292 -0
  56. package/docs/explanation/hot-module-replacement.md +137 -0
  57. package/docs/explanation/hydration.md +14 -0
  58. package/docs/explanation/index-query-param.md +86 -0
  59. package/docs/explanation/index.md +4 -0
  60. package/docs/explanation/lazy-route-discovery.md +78 -0
  61. package/docs/explanation/location.md +6 -0
  62. package/docs/explanation/progressive-enhancement.md +150 -0
  63. package/docs/explanation/race-conditions.md +88 -0
  64. package/docs/explanation/react-transitions.md +160 -0
  65. package/docs/explanation/route-matching.md +7 -0
  66. package/docs/explanation/server-client-execution.md +4 -0
  67. package/docs/explanation/sessions-and-cookies.md +465 -0
  68. package/docs/explanation/special-files.md +16 -0
  69. package/docs/explanation/state-management.md +524 -0
  70. package/docs/explanation/styling.md +87 -0
  71. package/docs/explanation/type-safety.md +82 -0
  72. package/docs/how-to/accessibility.md +44 -0
  73. package/docs/how-to/client-data.md +199 -0
  74. package/docs/how-to/data-strategy.md +317 -0
  75. package/docs/how-to/error-boundary.md +231 -0
  76. package/docs/how-to/error-reporting.md +142 -0
  77. package/docs/how-to/fetchers.md +307 -0
  78. package/docs/how-to/file-route-conventions.md +410 -0
  79. package/docs/how-to/file-uploads.md +217 -0
  80. package/docs/how-to/form-validation.md +120 -0
  81. package/docs/how-to/headers.md +164 -0
  82. package/docs/how-to/index.md +4 -0
  83. package/docs/how-to/instrumentation.md +556 -0
  84. package/docs/how-to/meta.md +40 -0
  85. package/docs/how-to/middleware.md +763 -0
  86. package/docs/how-to/navigation-blocking.md +233 -0
  87. package/docs/how-to/optimize-revalidation.md +12 -0
  88. package/docs/how-to/pre-rendering.md +225 -0
  89. package/docs/how-to/presets.md +103 -0
  90. package/docs/how-to/react-server-components.md +899 -0
  91. package/docs/how-to/resource-routes.md +126 -0
  92. package/docs/how-to/route-module-type-safety.md +100 -0
  93. package/docs/how-to/search-params.md +4 -0
  94. package/docs/how-to/security.md +30 -0
  95. package/docs/how-to/server-bundles.md +66 -0
  96. package/docs/how-to/spa.md +120 -0
  97. package/docs/how-to/status.md +63 -0
  98. package/docs/how-to/suspense.md +132 -0
  99. package/docs/how-to/using-handle.md +117 -0
  100. package/docs/how-to/view-transitions.md +237 -0
  101. package/docs/how-to/webhook.md +50 -0
  102. package/docs/index.md +39 -0
  103. package/docs/start/data/actions.md +138 -0
  104. package/docs/start/data/custom.md +198 -0
  105. package/docs/start/data/data-loading.md +44 -0
  106. package/docs/start/data/index.md +4 -0
  107. package/docs/start/data/installation.md +52 -0
  108. package/docs/start/data/navigating.md +12 -0
  109. package/docs/start/data/pending-ui.md +12 -0
  110. package/docs/start/data/route-object.md +268 -0
  111. package/docs/start/data/routing.md +281 -0
  112. package/docs/start/data/testing.md +8 -0
  113. package/docs/start/declarative/index.md +4 -0
  114. package/docs/start/declarative/installation.md +43 -0
  115. package/docs/start/declarative/navigating.md +133 -0
  116. package/docs/start/declarative/routing.md +237 -0
  117. package/docs/start/declarative/url-values.md +65 -0
  118. package/docs/start/framework/actions.md +174 -0
  119. package/docs/start/framework/data-loading.md +201 -0
  120. package/docs/start/framework/deploying.md +96 -0
  121. package/docs/start/framework/index.md +4 -0
  122. package/docs/start/framework/installation.md +41 -0
  123. package/docs/start/framework/navigating.md +182 -0
  124. package/docs/start/framework/pending-ui.md +142 -0
  125. package/docs/start/framework/rendering.md +59 -0
  126. package/docs/start/framework/route-module.md +527 -0
  127. package/docs/start/framework/routing.md +362 -0
  128. package/docs/start/framework/testing.md +133 -0
  129. package/docs/start/index.md +4 -0
  130. package/docs/start/modes.md +201 -0
  131. package/docs/upgrading/component-routes.md +363 -0
  132. package/docs/upgrading/future.md +280 -0
  133. package/docs/upgrading/index.md +4 -0
  134. package/docs/upgrading/remix.md +403 -0
  135. package/docs/upgrading/router-provider.md +442 -0
  136. package/docs/upgrading/v6.md +382 -0
  137. package/package.json +2 -1
@@ -0,0 +1,362 @@
1
+ ---
2
+ title: Routing
3
+ order: 2
4
+ ---
5
+
6
+ # Routing
7
+
8
+ [MODES: framework]
9
+
10
+ ## Configuring Routes
11
+
12
+ Routes are configured in `app/routes.ts`. Each route has two required parts: a URL pattern to match the URL, and a file path to the route module that defines its behavior.
13
+
14
+ ```ts filename=app/routes.ts
15
+ import {
16
+ type RouteConfig,
17
+ route,
18
+ } from "@react-router/dev/routes";
19
+
20
+ export default [
21
+ route("some/path", "./some/file.tsx"),
22
+ // pattern ^ ^ module file
23
+ ] satisfies RouteConfig;
24
+ ```
25
+
26
+ Here is a larger sample route config:
27
+
28
+ ```ts filename=app/routes.ts
29
+ import {
30
+ type RouteConfig,
31
+ route,
32
+ index,
33
+ layout,
34
+ prefix,
35
+ } from "@react-router/dev/routes";
36
+
37
+ export default [
38
+ index("./home.tsx"),
39
+ route("about", "./about.tsx"),
40
+
41
+ layout("./auth/layout.tsx", [
42
+ route("login", "./auth/login.tsx"),
43
+ route("register", "./auth/register.tsx"),
44
+ ]),
45
+
46
+ ...prefix("concerts", [
47
+ index("./concerts/home.tsx"),
48
+ route(":city", "./concerts/city.tsx"),
49
+ route("trending", "./concerts/trending.tsx"),
50
+ ]),
51
+ ] satisfies RouteConfig;
52
+ ```
53
+
54
+ If you prefer to define your routes via file naming conventions rather than configuration, the `@react-router/fs-routes` package provides a [file system routing convention][file-route-conventions]. You can even combine different routing conventions if you like:
55
+
56
+ ```ts filename=app/routes.ts
57
+ import {
58
+ type RouteConfig,
59
+ route,
60
+ } from "@react-router/dev/routes";
61
+ import { flatRoutes } from "@react-router/fs-routes";
62
+
63
+ export default [
64
+ route("/", "./home.tsx"),
65
+
66
+ ...(await flatRoutes()),
67
+ ] satisfies RouteConfig;
68
+ ```
69
+
70
+ ## Route Modules
71
+
72
+ The files referenced in `routes.ts` define each route's behavior:
73
+
74
+ ```tsx filename=app/routes.ts
75
+ route("teams/:teamId", "./team.tsx"),
76
+ // route module ^^^^^^^^
77
+ ```
78
+
79
+ Here's a sample route module:
80
+
81
+ ```tsx filename=app/team.tsx
82
+ // provides type safety/inference
83
+ import type { Route } from "./+types/team";
84
+
85
+ // provides `loaderData` to the component
86
+ export async function loader({ params }: Route.LoaderArgs) {
87
+ let team = await fetchTeam(params.teamId);
88
+ return { name: team.name };
89
+ }
90
+
91
+ // renders after the loader is done
92
+ export default function Component({
93
+ loaderData,
94
+ }: Route.ComponentProps) {
95
+ return <h1>{loaderData.name}</h1>;
96
+ }
97
+ ```
98
+
99
+ Route modules have more features like actions, headers, and error boundaries, but they will be covered in the next guide: [Route Modules](./route-module)
100
+
101
+ ## Nested Routes
102
+
103
+ Routes can be nested inside parent routes.
104
+
105
+ ```ts filename=app/routes.ts
106
+ import {
107
+ type RouteConfig,
108
+ route,
109
+ index,
110
+ } from "@react-router/dev/routes";
111
+
112
+ export default [
113
+ // parent route
114
+ route("dashboard", "./dashboard.tsx", [
115
+ // child routes
116
+ index("./home.tsx"),
117
+ route("settings", "./settings.tsx"),
118
+ ]),
119
+ ] satisfies RouteConfig;
120
+ ```
121
+
122
+ The path of the parent is automatically included in the child, so this config creates both `"/dashboard"` and `"/dashboard/settings"` URLs.
123
+
124
+ Child routes are rendered through the `<Outlet/>` in the parent route.
125
+
126
+ ```tsx filename=app/dashboard.tsx
127
+ import { Outlet } from "react-router";
128
+
129
+ export default function Dashboard() {
130
+ return (
131
+ <div>
132
+ <h1>Dashboard</h1>
133
+ {/* will either be home.tsx or settings.tsx */}
134
+ <Outlet />
135
+ </div>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ## Root Route
141
+
142
+ Every route in `routes.ts` is nested inside the special `app/root.tsx` module.
143
+
144
+ ## Layout Routes
145
+
146
+ Using `layout`, layout routes create new nesting for their children, but they don't add any segments to the URL. It's like the root route but they can be added at any level.
147
+
148
+ ```tsx filename=app/routes.ts lines=[10,16]
149
+ import {
150
+ type RouteConfig,
151
+ route,
152
+ layout,
153
+ index,
154
+ prefix,
155
+ } from "@react-router/dev/routes";
156
+
157
+ export default [
158
+ layout("./marketing/layout.tsx", [
159
+ index("./marketing/home.tsx"),
160
+ route("contact", "./marketing/contact.tsx"),
161
+ ]),
162
+ ...prefix("projects", [
163
+ index("./projects/home.tsx"),
164
+ layout("./projects/project-layout.tsx", [
165
+ route(":pid", "./projects/project.tsx"),
166
+ route(":pid/edit", "./projects/edit-project.tsx"),
167
+ ]),
168
+ ]),
169
+ ] satisfies RouteConfig;
170
+ ```
171
+
172
+ Note that:
173
+
174
+ - `home.tsx` and `contact.tsx` will be rendered into the `marketing/layout.tsx` outlet without creating any new URL paths
175
+ - `project.tsx` and `edit-project.tsx` will be rendered into the `projects/project-layout.tsx` outlet at `/projects/:pid` and `/projects/:pid/edit` while `projects/home.tsx` will not.
176
+
177
+ ## Index Routes
178
+
179
+ ```ts
180
+ index(componentFile),
181
+ ```
182
+
183
+ Index routes render into their parent's [Outlet][outlet] at their parent's URL (like a default child route).
184
+
185
+ ```ts filename=app/routes.ts
186
+ import {
187
+ type RouteConfig,
188
+ route,
189
+ index,
190
+ } from "@react-router/dev/routes";
191
+
192
+ export default [
193
+ // renders into the root.tsx Outlet at /
194
+ index("./home.tsx"),
195
+ route("dashboard", "./dashboard.tsx", [
196
+ // renders into the dashboard.tsx Outlet at /dashboard
197
+ index("./dashboard-home.tsx"),
198
+ route("settings", "./dashboard-settings.tsx"),
199
+ ]),
200
+ ] satisfies RouteConfig;
201
+ ```
202
+
203
+ Note that index routes can't have children.
204
+
205
+ ## Route Prefixes
206
+
207
+ Using `prefix`, you can add a path prefix to a set of routes without needing to introduce a parent route.
208
+
209
+ ```tsx filename=app/routes.ts lines=[14]
210
+ import {
211
+ type RouteConfig,
212
+ route,
213
+ layout,
214
+ index,
215
+ prefix,
216
+ } from "@react-router/dev/routes";
217
+
218
+ export default [
219
+ layout("./marketing/layout.tsx", [
220
+ index("./marketing/home.tsx"),
221
+ route("contact", "./marketing/contact.tsx"),
222
+ ]),
223
+ ...prefix("projects", [
224
+ index("./projects/home.tsx"),
225
+ layout("./projects/project-layout.tsx", [
226
+ route(":pid", "./projects/project.tsx"),
227
+ route(":pid/edit", "./projects/edit-project.tsx"),
228
+ ]),
229
+ ]),
230
+ ] satisfies RouteConfig;
231
+ ```
232
+
233
+ Note that this does not introduce a new route into the route tree. Instead, it merely modifies the paths of its children.
234
+
235
+ For example, these two sets of routes are equivalent:
236
+
237
+ ```ts filename=app/routes.ts
238
+ // This usage of `prefix`...
239
+ prefix("parent", [
240
+ route("child1", "./child1.tsx"),
241
+ route("child2", "./child2.tsx"),
242
+ ])
243
+
244
+ // ...is equivalent to this:
245
+ [
246
+ route("parent/child1", "./child1.tsx"),
247
+ route("parent/child2", "./child2.tsx"),
248
+ ]
249
+ ```
250
+
251
+ ## Dynamic Segments
252
+
253
+ If a path segment starts with `:` then it becomes a "dynamic segment". When the route matches the URL, the dynamic segment will be parsed from the URL and provided as `params` to other router APIs.
254
+
255
+ ```ts filename=app/routes.ts
256
+ route("teams/:teamId", "./team.tsx"),
257
+ ```
258
+
259
+ ```tsx filename=app/team.tsx
260
+ import type { Route } from "./+types/team";
261
+
262
+ export async function loader({ params }: Route.LoaderArgs) {
263
+ // ^? { teamId: string }
264
+ }
265
+
266
+ export default function Component({
267
+ params,
268
+ }: Route.ComponentProps) {
269
+ params.teamId;
270
+ // ^ string
271
+ }
272
+ ```
273
+
274
+ You can have multiple dynamic segments in one route path:
275
+
276
+ ```ts filename=app/routes.ts
277
+ route("c/:categoryId/p/:productId", "./product.tsx"),
278
+ ```
279
+
280
+ ```tsx filename=app/product.tsx
281
+ import type { Route } from "./+types/product";
282
+
283
+ async function loader({ params }: LoaderArgs) {
284
+ // ^? { categoryId: string; productId: string }
285
+ }
286
+ ```
287
+
288
+ ## Optional Segments
289
+
290
+ You can make a route segment optional by adding a `?` to the end of the segment.
291
+
292
+ ```ts filename=app/routes.ts
293
+ route(":lang?/categories", "./categories.tsx"),
294
+ ```
295
+
296
+ You can have optional static segments, too:
297
+
298
+ ```ts filename=app/routes.ts
299
+ route("users/:userId/edit?", "./user.tsx");
300
+ ```
301
+
302
+ ## Splats
303
+
304
+ Also known as "catchall" and "star" segments. If a route path pattern ends with `/*` then it will match any characters following the `/`, including other `/` characters.
305
+
306
+ ```ts filename=app/routes.ts
307
+ route("files/*", "./files.tsx"),
308
+ ```
309
+
310
+ ```tsx filename=app/files.tsx
311
+ export async function loader({ params }: Route.LoaderArgs) {
312
+ // params["*"] will contain the remaining URL after files/
313
+ }
314
+ ```
315
+
316
+ You can destructure the `*`, you just have to assign it a new name. A common name is `splat`:
317
+
318
+ ```tsx
319
+ const { "*": splat } = params;
320
+ ```
321
+
322
+ You can also use a splat to catch requests that don't match any route:
323
+
324
+ ```ts filename=app/routes.ts
325
+ route("*", "./catchall.tsx"); // catchall route,
326
+ ```
327
+
328
+ ```tsx filename=app/catchall.tsx
329
+ export function loader() {
330
+ throw new Response("Page not found", { status: 404 });
331
+ }
332
+ ```
333
+
334
+ ## Component Routes
335
+
336
+ You can also use components that match the URL to elements anywhere in the component tree:
337
+
338
+ ```tsx
339
+ import { Routes, Route } from "react-router";
340
+
341
+ function Wizard() {
342
+ return (
343
+ <div>
344
+ <h1>Some Wizard with Steps</h1>
345
+ <Routes>
346
+ <Route index element={<StepOne />} />
347
+ <Route path="step-2" element={<StepTwo />} />
348
+ <Route path="step-3" element={<StepThree />} />
349
+ </Routes>
350
+ </div>
351
+ );
352
+ }
353
+ ```
354
+
355
+ Note that these routes do not participate in data loading, actions, code splitting, or any other route module features, so their use cases are more limited than those of the route module.
356
+
357
+ ---
358
+
359
+ Next: [Route Module](./route-module)
360
+
361
+ [file-route-conventions]: ../../how-to/file-route-conventions
362
+ [outlet]: https://api.reactrouter.com/v7/functions/react-router.Outlet.html
@@ -0,0 +1,133 @@
1
+ ---
2
+ title: Testing
3
+ order: 9
4
+ ---
5
+
6
+ # Testing
7
+
8
+ [MODES: framework, data]
9
+
10
+ ## Introduction
11
+
12
+ When components use things like `useLoaderData`, `<Link>`, etc, they are required to be rendered in context of a React Router app. The `createRoutesStub` function creates that context to test components in isolation.
13
+
14
+ Consider a login form component that relies on `useActionData`
15
+
16
+ ```tsx
17
+ import { useActionData } from "react-router";
18
+
19
+ export function LoginForm() {
20
+ const actionData = useActionData();
21
+ const errors = actionData?.errors;
22
+ return (
23
+ <Form method="post">
24
+ <label>
25
+ <input type="text" name="username" />
26
+ {errors?.username && <div>{errors.username}</div>}
27
+ </label>
28
+
29
+ <label>
30
+ <input type="password" name="password" />
31
+ {errors?.password && <div>{errors.password}</div>}
32
+ </label>
33
+
34
+ <button type="submit">Login</button>
35
+ </Form>
36
+ );
37
+ }
38
+ ```
39
+
40
+ We can test this component with `createRoutesStub`. It takes an array of objects that resemble route modules with loaders, actions, and components.
41
+
42
+ ```tsx
43
+ import { createRoutesStub } from "react-router";
44
+ import {
45
+ render,
46
+ screen,
47
+ waitFor,
48
+ } from "@testing-library/react";
49
+ import userEvent from "@testing-library/user-event";
50
+ import { LoginForm } from "./LoginForm";
51
+
52
+ test("LoginForm renders error messages", async () => {
53
+ const USER_MESSAGE = "Username is required";
54
+ const PASSWORD_MESSAGE = "Password is required";
55
+
56
+ const Stub = createRoutesStub([
57
+ {
58
+ path: "/login",
59
+ Component: LoginForm,
60
+ action() {
61
+ return {
62
+ errors: {
63
+ username: USER_MESSAGE,
64
+ password: PASSWORD_MESSAGE,
65
+ },
66
+ };
67
+ },
68
+ },
69
+ ]);
70
+
71
+ // render the app stub at "/login"
72
+ render(<Stub initialEntries={["/login"]} />);
73
+
74
+ // simulate interactions
75
+ userEvent.click(screen.getByText("Login"));
76
+ await waitFor(() => screen.findByText(USER_MESSAGE));
77
+ await waitFor(() => screen.findByText(PASSWORD_MESSAGE));
78
+ });
79
+ ```
80
+
81
+ ## Using with Framework Mode Types
82
+
83
+ It's important to note that `createRoutesStub` is designed for _unit_ testing of reusable components in your application that rely on contextual router information (i.e., `loaderData`, `actionData`, `matches`). These components usually obtain this information via the hooks (`useLoaderData`, `useActionData`, `useMatches`) or via props passed down from the ancestor route component. We **strongly** recommend limiting your usage of `createRoutesStub` to unit testing of these types of reusable components.
84
+
85
+ `createRoutesStub` is _not designed_ for (and is arguably incompatible with) direct testing of Route components using the [`Route.\*`](../../explanation/type-safety) types available in Framework Mode. This is because the `Route.*` types are derived from your actual application - including the real `loader`/`action` functions as well as the structure of your route tree structure (which defines the `matches` type). When you use `createRoutesStub`, you are providing stubbed values for `loaderData`, `actionData`, and even your `matches` based on the route tree you pass to `createRoutesStub`. Therefore, the types won't align with the `Route.*` types and you'll get type issues trying to use a route component in a route stub.
86
+
87
+ ```tsx filename=routes/login.tsx
88
+ export default function Login({
89
+ actionData,
90
+ }: Route.ComponentProps) {
91
+ return <Form method="post">...</Form>;
92
+ }
93
+ ```
94
+
95
+ ```tsx filename=routes/login.test.tsx
96
+ import LoginRoute from "./login";
97
+
98
+ test("LoginRoute renders error messages", async () => {
99
+ const Stub = createRoutesStub([
100
+ {
101
+ path: "/login",
102
+ Component: LoginRoute,
103
+ // ^ ❌ Types of property 'matches' are incompatible.
104
+ action() {
105
+ /*...*/
106
+ },
107
+ },
108
+ ]);
109
+
110
+ // ...
111
+ });
112
+ ```
113
+
114
+ These type errors are generally accurate if you try to setup your tests like this. As long as your stubbed `loader`/`action` functions match your real implementations, then the types for `loaderData`/`actionData` will be correct, but if they differ your types will be lying to you.
115
+
116
+ `matches` is more complicated since you don't usually stub out all of the ancestor routes. In this example, there is no `root` route so `matches` will only contain your test route, while it will contain the root route and any other ancestors at runtime. There's no great way to automatically align the typegen types with the runtime types in your test.
117
+
118
+ Therefore, if you need to test Route level components, we recommend you do that via an Integration/E2E test (Playwright, Cypress, etc.) against a running application because you're venturing out of unit testing territory when testing your route as a whole.
119
+
120
+ If you _need_ to write a unit test against the route, you can add a `@ts-expect-error` comment in your test to silence the TypeScript error:
121
+
122
+ ```tsx
123
+ const Stub = createRoutesStub([
124
+ {
125
+ path: "/login",
126
+ // @ts-expect-error: `matches` won't align between test code and app code
127
+ Component: LoginRoute,
128
+ action() {
129
+ /*...*/
130
+ },
131
+ },
132
+ ]);
133
+ ```
@@ -0,0 +1,4 @@
1
+ ---
2
+ title: Getting Started
3
+ order: 1
4
+ ---
@@ -0,0 +1,201 @@
1
+ ---
2
+ title: Picking a Mode
3
+ order: 1
4
+ ---
5
+
6
+ # Picking a Mode
7
+
8
+ React Router is a multi-strategy router for React. There are three primary ways, or "modes", to use it in your app. Across the docs you'll see these icons indicating which mode the content is relevant to:
9
+
10
+ [MODES: framework, data, declarative]
11
+
12
+ <p></p>
13
+
14
+ The features available in each mode are additive, so moving from Declarative to Data to Framework simply adds more features at the cost of architectural control. So pick your mode based on how much control or how much help you want from React Router.
15
+
16
+ The mode depends on which "top level" router API you're using:
17
+
18
+ ## Declarative
19
+
20
+ Declarative mode enables basic routing features like matching URLs to components, navigating around the app, and providing active states with APIs like `<Link>`, `useNavigate`, and `useLocation`.
21
+
22
+ ```tsx
23
+ import { BrowserRouter } from "react-router";
24
+
25
+ ReactDOM.createRoot(root).render(
26
+ <BrowserRouter>
27
+ <App />
28
+ </BrowserRouter>,
29
+ );
30
+ ```
31
+
32
+ ## Data
33
+
34
+ By moving route configuration outside of React rendering, Data Mode adds data loading, actions, pending states and more with APIs like `loader`, `action`, and `useFetcher`.
35
+
36
+ ```tsx
37
+ import {
38
+ createBrowserRouter,
39
+ RouterProvider,
40
+ } from "react-router";
41
+
42
+ let router = createBrowserRouter([
43
+ {
44
+ path: "/",
45
+ Component: Root,
46
+ loader: loadRootData,
47
+ },
48
+ ]);
49
+
50
+ ReactDOM.createRoot(root).render(
51
+ <RouterProvider router={router} />,
52
+ );
53
+ ```
54
+
55
+ ## Framework
56
+
57
+ Framework Mode wraps Data Mode with a Vite plugin to add the full React Router experience with:
58
+
59
+ - type-safe `href`
60
+ - type-safe Route Module API
61
+ - intelligent code splitting
62
+ - SPA, SSR, and static rendering strategies
63
+ - and more
64
+
65
+ ```ts filename=routes.ts
66
+ import { index, route } from "@react-router/dev/routes";
67
+
68
+ export default [
69
+ index("./home.tsx"),
70
+ route("products/:pid", "./product.tsx"),
71
+ ];
72
+ ```
73
+
74
+ You'll then have access to the Route Module API with type-safe params, loaderData, code splitting, SPA/SSR/SSG strategies, and more.
75
+
76
+ ```ts filename=product.tsx
77
+ import { Route } from "./+types/product.tsx";
78
+
79
+ export async function loader({ params }: Route.LoaderArgs) {
80
+ let product = await getProduct(params.pid);
81
+ return { product };
82
+ }
83
+
84
+ export default function Product({
85
+ loaderData,
86
+ }: Route.ComponentProps) {
87
+ return <div>{loaderData.product.name}</div>;
88
+ }
89
+ ```
90
+
91
+ ## Decision Advice
92
+
93
+ Every mode supports any architecture and deployment target, so the question isn't really about if you want SSR, SPA, etc. It's about how much you want to do yourself.
94
+
95
+ **Use Framework Mode if you:**
96
+
97
+ - are too new to have an opinion
98
+ - are considering Next.js, Solid Start, SvelteKit, Astro, TanStack Start, etc. and want to compare
99
+ - just want to build something with React
100
+ - might want to server render, might not
101
+ - are coming from Remix (React Router v7 is the "next version" after Remix v2)
102
+ - are migrating from Next.js
103
+
104
+ [→ Get Started with Framework Mode](./framework/installation).
105
+
106
+ **Use Data Mode if you:**
107
+
108
+ - want data features but also want to have control over bundling, data, and server abstractions
109
+ - started a data router in v6.4 and are happy with it
110
+
111
+ [→ Get Started with Data Mode](./data/custom).
112
+
113
+ **Use Declarative Mode if you:**
114
+
115
+ - want to use React Router as simply as possible
116
+ - are coming from v6 and are happy with the `<BrowserRouter>`
117
+ - have a data layer that either skips pending states (like local first, background data replication/sync) or has its own abstractions for them
118
+ - are coming from Create React App (you may want to consider framework mode though)
119
+
120
+ [→ Get Started with Declarative Mode](./declarative/installation).
121
+
122
+ ## API + Mode Availability Table
123
+
124
+ This is mostly for the LLMs, but knock yourself out:
125
+
126
+ | API | Framework | Data | Declarative |
127
+ | ------------------------------ | --------- | ---- | ----------- |
128
+ | Await | ✅ | ✅ | |
129
+ | Form | ✅ | ✅ |
130
+ | Link | ✅ | ✅ | ✅ |
131
+ | `<Link discover>` | ✅ | | |
132
+ | `<Link prefetch>` | ✅ | | |
133
+ | `<Link preventScrollReset>` | ✅ | ✅ | |
134
+ | Links | ✅ | | |
135
+ | Meta | ✅ | | |
136
+ | NavLink | ✅ | ✅ | ✅ |
137
+ | `<NavLink discover>` | ✅ | | |
138
+ | `<NavLink prefetch>` | ✅ | | |
139
+ | `<NavLink preventScrollReset>` | ✅ | ✅ | |
140
+ | NavLink `isPending` | ✅ | ✅ | |
141
+ | Navigate | ✅ | ✅ | ✅ |
142
+ | Outlet | ✅ | ✅ | ✅ |
143
+ | PrefetchPageLinks | ✅ | | |
144
+ | Route | ✅ | ✅ | ✅ |
145
+ | Routes | ✅ | ✅ | ✅ |
146
+ | Scripts | ✅ | | |
147
+ | ScrollRestoration | ✅ | ✅ | |
148
+ | ServerRouter | ✅ | | |
149
+ | usePrompt | ✅ | ✅ | |
150
+ | useActionData | ✅ | ✅ | |
151
+ | useAsyncError | ✅ | ✅ | |
152
+ | useAsyncValue | ✅ | ✅ | |
153
+ | useBeforeUnload | ✅ | ✅ | ✅ |
154
+ | useBlocker | ✅ | ✅ | |
155
+ | useFetcher | ✅ | ✅ | |
156
+ | useFetchers | ✅ | ✅ | |
157
+ | useFormAction | ✅ | ✅ | |
158
+ | useHref | ✅ | ✅ | ✅ |
159
+ | useInRouterContext | ✅ | ✅ | ✅ |
160
+ | useLinkClickHandler | ✅ | ✅ | ✅ |
161
+ | useLoaderData | ✅ | ✅ | |
162
+ | useLocation | ✅ | ✅ | ✅ |
163
+ | useMatch | ✅ | ✅ | ✅ |
164
+ | useMatches | ✅ | ✅ | |
165
+ | useNavigate | ✅ | ✅ | ✅ |
166
+ | useNavigation | ✅ | ✅ | |
167
+ | useNavigationType | ✅ | ✅ | ✅ |
168
+ | useOutlet | ✅ | ✅ | ✅ |
169
+ | useOutletContext | ✅ | ✅ | ✅ |
170
+ | useParams | ✅ | ✅ | ✅ |
171
+ | useResolvedPath | ✅ | ✅ | ✅ |
172
+ | useRevalidator | ✅ | ✅ | |
173
+ | useRouteError | ✅ | ✅ | |
174
+ | useRouteLoaderData | ✅ | ✅ | |
175
+ | useRoutes | ✅ | ✅ | ✅ |
176
+ | useSearchParams | ✅ | ✅ | ✅ |
177
+ | useSubmit | ✅ | ✅ | |
178
+ | useViewTransitionState | ✅ | ✅ | |
179
+ | isCookieFunction | ✅ | ✅ | |
180
+ | isSessionFunction | ✅ | ✅ | |
181
+ | createCookie | ✅ | ✅ | |
182
+ | createCookieSessionStorage | ✅ | ✅ | |
183
+ | createMemorySessionStorage | ✅ | ✅ | |
184
+ | createPath | ✅ | ✅ | ✅ |
185
+ | createRoutesFromElements | | ✅ | |
186
+ | createRoutesStub | ✅ | ✅ | |
187
+ | createSearchParams | ✅ | ✅ | ✅ |
188
+ | data | ✅ | ✅ | |
189
+ | generatePath | ✅ | ✅ | ✅ |
190
+ | href | ✅ | | |
191
+ | isCookie | ✅ | ✅ | |
192
+ | isRouteErrorResponse | ✅ | ✅ | |
193
+ | isSession | ✅ | ✅ | |
194
+ | matchPath | ✅ | ✅ | ✅ |
195
+ | matchRoutes | ✅ | ✅ | ✅ |
196
+ | parsePath | ✅ | ✅ | ✅ |
197
+ | redirect | ✅ | ✅ | |
198
+ | redirectDocument | ✅ | ✅ | |
199
+ | renderMatches | ✅ | ✅ | ✅ |
200
+ | replace | ✅ | ✅ | |
201
+ | resolvePath | ✅ | ✅ | ✅ |