UNPKG

9.04 kB Markdown View Raw
1---
2title: Routing
3order: 2
4---
5
6# Routing
7
8[MODES: framework]
9
10## Configuring Routes
11
12Routes 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
15import {
16 type RouteConfig,
17 route,
18} from "@react-router/dev/routes";
19
20export default [
21 route("some/path", "./some/file.tsx"),
22 // pattern ^ ^ module file
23] satisfies RouteConfig;
24```
25
26Here is a larger sample route config:
27
28```ts filename=app/routes.ts
29import {
30 type RouteConfig,
31 route,
32 index,
33 layout,
34 prefix,
35} from "@react-router/dev/routes";
36
37export 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
54If 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
57import {
58 type RouteConfig,
59 route,
60} from "@react-router/dev/routes";
61import { flatRoutes } from "@react-router/fs-routes";
62
63export default [
64 route("/", "./home.tsx"),
65
66 ...(await flatRoutes()),
67] satisfies RouteConfig;
68```
69
70## Route Modules
71
72The files referenced in `routes.ts` define each route's behavior:
73
74```tsx filename=app/routes.ts
75route("teams/:teamId", "./team.tsx"),
76// route module ^^^^^^^^
77```
78
79Here's a sample route module:
80
81```tsx filename=app/team.tsx
82// provides type safety/inference
83import type { Route } from "./+types/team";
84
85// provides `loaderData` to the component
86export 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
92export default function Component({
93 loaderData,
94}: Route.ComponentProps) {
95 return <h1>{loaderData.name}</h1>;
96}
97```
98
99Route 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
103Routes can be nested inside parent routes.
104
105```ts filename=app/routes.ts
106import {
107 type RouteConfig,
108 route,
109 index,
110} from "@react-router/dev/routes";
111
112export 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
122The path of the parent is automatically included in the child, so this config creates both `"/dashboard"` and `"/dashboard/settings"` URLs.
123
124Child routes are rendered through the `<Outlet/>` in the parent route.
125
126```tsx filename=app/dashboard.tsx
127import { Outlet } from "react-router";
128
129export 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
142Every route in `routes.ts` is nested inside the special `app/root.tsx` module.
143
144## Layout Routes
145
146Using `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]
149import {
150 type RouteConfig,
151 route,
152 layout,
153 index,
154 prefix,
155} from "@react-router/dev/routes";
156
157export 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
172Note 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
180index(componentFile),
181```
182
183Index 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
186import {
187 type RouteConfig,
188 route,
189 index,
190} from "@react-router/dev/routes";
191
192export 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
203Note that index routes can't have children.
204
205## Route Prefixes
206
207Using `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]
210import {
211 type RouteConfig,
212 route,
213 layout,
214 index,
215 prefix,
216} from "@react-router/dev/routes";
217
218export 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
233Note that this does not introduce a new route into the route tree. Instead, it merely modifies the paths of its children.
234
235For example, these two sets of routes are equivalent:
236
237```ts filename=app/routes.ts
238// This usage of `prefix`...
239prefix("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
253If 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
256route("teams/:teamId", "./team.tsx"),
257```
258
259```tsx filename=app/team.tsx
260import type { Route } from "./+types/team";
261
262export async function loader({ params }: Route.LoaderArgs) {
263 // ^? { teamId: string }
264}
265
266export default function Component({
267 params,
268}: Route.ComponentProps) {
269 params.teamId;
270 // ^ string
271}
272```
273
274You can have multiple dynamic segments in one route path:
275
276```ts filename=app/routes.ts
277route("c/:categoryId/p/:productId", "./product.tsx"),
278```
279
280```tsx filename=app/product.tsx
281import type { Route } from "./+types/product";
282
283async function loader({ params }: LoaderArgs) {
284 // ^? { categoryId: string; productId: string }
285}
286```
287
288## Optional Segments
289
290You can make a route segment optional by adding a `?` to the end of the segment.
291
292```ts filename=app/routes.ts
293route(":lang?/categories", "./categories.tsx"),
294```
295
296You can have optional static segments, too:
297
298```ts filename=app/routes.ts
299route("users/:userId/edit?", "./user.tsx");
300```
301
302## Splats
303
304Also 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
307route("files/*", "./files.tsx"),
308```
309
310```tsx filename=app/files.tsx
311export async function loader({ params }: Route.LoaderArgs) {
312 // params["*"] will contain the remaining URL after files/
313}
314```
315
316You can destructure the `*`, you just have to assign it a new name. A common name is `splat`:
317
318```tsx
319const { "*": splat } = params;
320```
321
322You can also use a splat to catch requests that don't match any route:
323
324```ts filename=app/routes.ts
325route("*", "./catchall.tsx"); // catchall route,
326```
327
328```tsx filename=app/catchall.tsx
329export function loader() {
330 throw new Response("Page not found", { status: 404 });
331}
332```
333
334## Component Routes
335
336You can also use components that match the URL to elements anywhere in the component tree:
337
338```tsx
339import { Routes, Route } from "react-router";
340
341function 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
355Note 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
359Next: [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