0
# Advanced Routing
1
2
Advanced routing patterns including redirects, conditional rendering, and route matching utilities.
3
4
## Capabilities
5
6
### Redirect Component
7
8
Declarative redirect component for programmatic navigation during rendering.
9
10
```javascript { .api }
11
/**
12
* Declarative redirect component for programmatic navigation
13
* @param props - Redirect configuration
14
*/
15
function Redirect(props: {
16
from?: string;
17
to: string;
18
replace?: boolean;
19
state?: any;
20
noThrow?: boolean;
21
}): React.ReactElement;
22
```
23
24
**Props:**
25
- `from` (string, optional) - Source path pattern (only used inside Router)
26
- `to` (string, required) - Destination path
27
- `replace` (boolean, optional) - Replace current history entry (defaults to true)
28
- `state` (any, optional) - State to pass with redirect
29
- `noThrow` (boolean, optional) - Don't throw redirect error (for server rendering)
30
31
**Usage Examples:**
32
33
```javascript
34
import React from "react";
35
import { Router, Redirect } from "@reach/router";
36
37
// Basic redirect
38
const App = () => (
39
<Router>
40
<Home path="/" />
41
<Redirect from="/home" to="/" />
42
<About path="/about" />
43
</Router>
44
);
45
46
// Redirect with parameters
47
const App = () => (
48
<Router>
49
<UserProfile path="/users/:userId" />
50
<Redirect from="/profile/:userId" to="/users/:userId" />
51
</Router>
52
);
53
54
// Conditional redirect based on authentication
55
const ProtectedRoute = ({ component: Component, ...props }) => {
56
const isAuthenticated = useAuth();
57
58
if (!isAuthenticated) {
59
return <Redirect to="/login" state={{ from: props.path }} />;
60
}
61
62
return <Component {...props} />;
63
};
64
65
<Router>
66
<Login path="/login" />
67
<ProtectedRoute component={Dashboard} path="/dashboard" />
68
</Router>
69
70
// Redirect with state
71
const LoginRedirect = () => (
72
<Redirect
73
to="/dashboard"
74
state={{ message: "Successfully logged in!" }}
75
/>
76
);
77
78
// Server-safe redirect
79
const ServerSafeRedirect = ({ to }) => (
80
<Redirect to={to} noThrow />
81
);
82
83
// Multiple redirects for old URLs
84
const LegacyRedirects = () => (
85
<Router>
86
<Home path="/" />
87
<Redirect from="/old-home" to="/" />
88
<Redirect from="/legacy/about" to="/about" />
89
<Redirect from="/v1/users/:id" to="/users/:id" />
90
<About path="/about" />
91
<User path="/users/:id" />
92
</Router>
93
);
94
```
95
96
### Match Component
97
98
Render prop component for conditional rendering based on path matching without routing.
99
100
```javascript { .api }
101
/**
102
* Render prop component for conditional rendering based on path matching
103
* @param props - Match configuration
104
*/
105
function Match(props: {
106
path: string;
107
children: (props: MatchRenderProps) => React.ReactNode;
108
}): React.ReactElement;
109
110
interface MatchRenderProps {
111
navigate: (to: string | number, options?: NavigateOptions) => Promise<void>;
112
location: Location;
113
match: MatchResult | null;
114
}
115
116
interface MatchResult {
117
uri: string; // Always normalized with leading slash
118
path: string;
119
[paramName: string]: string | string[]; // Can contain arrays for splat routes
120
}
121
```
122
123
**Usage Examples:**
124
125
```javascript
126
import React from "react";
127
import { Match } from "@reach/router";
128
129
// Conditional navigation based on match
130
const ConditionalNav = () => (
131
<Match path="/admin/*">
132
{({ match }) => (
133
<nav>
134
<Link to="/">Home</Link>
135
{match && (
136
<>
137
<Link to="/admin/users">Users</Link>
138
<Link to="/admin/settings">Settings</Link>
139
</>
140
)}
141
</nav>
142
)}
143
</Match>
144
);
145
146
// Dynamic styling based on path match
147
const Header = () => (
148
<Match path="/products/*">
149
{({ match, location }) => (
150
<header className={match ? "products-header" : "default-header"}>
151
<h1>My Store</h1>
152
{match && <p>Browsing: {location.pathname}</p>}
153
</header>
154
)}
155
</Match>
156
);
157
158
// Multiple matches for complex layouts
159
const Layout = ({ children }) => (
160
<div>
161
<Match path="/dashboard/*">
162
{({ match }) => match && <DashboardSidebar />}
163
</Match>
164
165
<Match path="/admin/*">
166
{({ match }) => match && <AdminToolbar />}
167
</Match>
168
169
<main>{children}</main>
170
</div>
171
);
172
173
// Match with parameter extraction
174
const BreadcrumbTrail = () => (
175
<Match path="/users/:userId/posts/:postId">
176
{({ match, navigate }) => {
177
if (!match) return null;
178
179
const { userId, postId } = match;
180
181
return (
182
<nav className="breadcrumb">
183
<button onClick={() => navigate("/")}>Home</button>
184
<span> / </span>
185
<button onClick={() => navigate("/users")}>Users</button>
186
<span> / </span>
187
<button onClick={() => navigate(`/users/${userId}`)}>
188
User {userId}
189
</button>
190
<span> / </span>
191
<span>Post {postId}</span>
192
</nav>
193
);
194
}}
195
</Match>
196
);
197
198
// Conditional component mounting
199
const FeatureToggle = ({ feature, children }) => (
200
<Match path={`/features/${feature}/*`}>
201
{({ match }) => match ? children : null}
202
</Match>
203
);
204
205
<FeatureToggle feature="beta">
206
<BetaFeatures />
207
</FeatureToggle>
208
```
209
210
### matchPath Utility
211
212
Programmatic route matching utility for checking if a path matches a pattern outside of components.
213
214
```javascript { .api }
215
/**
216
* Programmatic route matching utility
217
* @param path - Path pattern to match against
218
* @param uri - URI to test for matching
219
* @returns Match result with params and uri, or null if no match
220
*/
221
function matchPath(path: string, uri: string): MatchResult | null;
222
223
interface MatchResult {
224
route: {
225
path: string;
226
};
227
params: Record<string, string | string[]>; // Can contain arrays for splat routes
228
uri: string; // Always normalized with leading slash
229
}
230
```
231
232
**Usage Examples:**
233
234
```javascript
235
import { matchPath } from "@reach/router";
236
237
// Basic path matching
238
const match = matchPath("/users/:userId", "/users/123");
239
if (match) {
240
console.log(match.params.userId); // "123"
241
console.log(match.uri); // "/users/123"
242
}
243
244
// Wildcard matching
245
const wildcardMatch = matchPath("/files/*", "/files/docs/readme.txt");
246
if (wildcardMatch) {
247
console.log(wildcardMatch.params["*"]); // "docs/readme.txt"
248
}
249
250
// Route-based data fetching
251
const fetchRouteData = async (pathname) => {
252
const userMatch = matchPath("/users/:userId", pathname);
253
if (userMatch) {
254
return await fetchUser(userMatch.params.userId);
255
}
256
257
const postMatch = matchPath("/posts/:postId", pathname);
258
if (postMatch) {
259
return await fetchPost(postMatch.params.postId);
260
}
261
262
return null;
263
};
264
265
// Server-side route matching
266
const getServerSideProps = async (context) => {
267
const { req } = context;
268
const pathname = req.url;
269
270
const match = matchPath("/products/:productId", pathname);
271
if (match) {
272
const product = await fetchProduct(match.params.productId);
273
return { props: { product } };
274
}
275
276
return { props: {} };
277
};
278
279
// Route guards and middleware
280
const routeGuards = [
281
{
282
pattern: "/admin/*",
283
guard: (match, user) => user?.isAdmin
284
},
285
{
286
pattern: "/users/:userId/*",
287
guard: (match, user) => user?.id === match.params.userId
288
}
289
];
290
291
const checkAccess = (pathname, user) => {
292
for (const { pattern, guard } of routeGuards) {
293
const match = matchPath(pattern, pathname);
294
if (match && !guard(match, user)) {
295
return false;
296
}
297
}
298
return true;
299
};
300
301
// Dynamic route configuration
302
const routes = [
303
{ pattern: "/", component: "Home" },
304
{ pattern: "/about", component: "About" },
305
{ pattern: "/users/:userId", component: "UserProfile" },
306
{ pattern: "/posts/:postId", component: "PostDetail" }
307
];
308
309
const findMatchingRoute = (pathname) => {
310
for (const route of routes) {
311
const match = matchPath(route.pattern, pathname);
312
if (match) {
313
return { ...route, match };
314
}
315
}
316
return null;
317
};
318
```
319
320
### Redirect Utilities
321
322
Utility functions for handling redirects programmatically.
323
324
```javascript { .api }
325
/**
326
* Check if an error is a redirect request
327
* @param error - Error object to check
328
* @returns True if error is a redirect request
329
*/
330
function isRedirect(error: any): boolean;
331
332
/**
333
* Throw a redirect request for error boundary handling
334
* @param to - Destination path
335
* @throws RedirectRequest that can be caught by error boundaries
336
*/
337
function redirectTo(to: string): never;
338
```
339
340
**Usage Examples:**
341
342
```javascript
343
import { isRedirect, redirectTo } from "@reach/router";
344
345
// Error boundary handling redirects
346
class RedirectBoundary extends React.Component {
347
componentDidCatch(error, errorInfo) {
348
if (isRedirect(error)) {
349
// Handle redirect in error boundary
350
console.log("Caught redirect to:", error.uri);
351
// The redirect will be handled automatically
352
} else {
353
// Handle other errors
354
console.error("Component error:", error);
355
}
356
}
357
358
render() {
359
return this.props.children;
360
}
361
}
362
363
// Programmatic redirects in components
364
const AuthCheck = ({ children }) => {
365
const user = useAuth();
366
367
React.useEffect(() => {
368
if (!user) {
369
// Throw redirect that will be caught by LocationProvider
370
redirectTo("/login");
371
}
372
}, [user]);
373
374
return user ? children : null;
375
};
376
377
// Conditional redirects in render
378
const ConditionalRedirect = ({ condition, to, children }) => {
379
if (condition) {
380
redirectTo(to);
381
}
382
383
return children;
384
};
385
386
// Redirect with data validation
387
const ValidatedRoute = ({ data, fallback, children }) => {
388
React.useEffect(() => {
389
if (!data || !data.isValid) {
390
redirectTo(fallback);
391
}
392
}, [data, fallback]);
393
394
return data?.isValid ? children : null;
395
};
396
397
// Error handling with redirect detection
398
const handleAsyncAction = async () => {
399
try {
400
await performAction();
401
} catch (error) {
402
if (isRedirect(error)) {
403
// Don't log redirect errors
404
return;
405
}
406
407
// Log and handle other errors
408
console.error("Action failed:", error);
409
redirectTo("/error");
410
}
411
};
412
413
// Custom redirect hook
414
const useRedirectIf = (condition, to) => {
415
React.useEffect(() => {
416
if (condition) {
417
redirectTo(to);
418
}
419
}, [condition, to]);
420
};
421
422
// Usage of custom hook
423
const ProtectedComponent = () => {
424
const user = useAuth();
425
useRedirectIf(!user, "/login");
426
427
return <div>Protected content</div>;
428
};
429
```
430
431
### Advanced Route Patterns
432
433
Complex routing patterns and techniques for sophisticated applications.
434
435
**Usage Examples:**
436
437
```javascript
438
// Route-based code splitting
439
const LazyRoute = ({ component, ...props }) => {
440
const [Component, setComponent] = React.useState(null);
441
442
React.useEffect(() => {
443
import(`../components/${component}`)
444
.then(module => setComponent(() => module.default));
445
}, [component]);
446
447
if (!Component) {
448
return <div>Loading...</div>;
449
}
450
451
return <Component {...props} />;
452
};
453
454
<Router>
455
<LazyRoute component="Home" path="/" />
456
<LazyRoute component="About" path="/about" />
457
</Router>
458
459
// Nested routing with context
460
const NestedRoutes = () => (
461
<Router>
462
<Dashboard path="/dashboard/*">
463
<DashboardHome path="/" />
464
<Settings path="/settings/*">
465
<GeneralSettings path="/" />
466
<SecuritySettings path="/security" />
467
<ProfileSettings path="/profile" />
468
</Settings>
469
</Dashboard>
470
</Router>
471
);
472
473
// Route-based theming
474
const ThemedRouter = () => {
475
const location = useLocation();
476
const theme = getThemeForPath(location.pathname);
477
478
return (
479
<ThemeProvider theme={theme}>
480
<Router>
481
<Home path="/" />
482
<Admin path="/admin/*" />
483
<Public path="/public/*" />
484
</Router>
485
</ThemeProvider>
486
);
487
};
488
489
// Dynamic route generation
490
const DynamicRoutes = ({ routes }) => (
491
<Router>
492
{routes.map(route => {
493
const Component = route.component;
494
return (
495
<Component
496
key={route.path}
497
path={route.path}
498
{...route.props}
499
/>
500
);
501
})}
502
</Router>
503
);
504
505
// Route transition animations
506
const AnimatedRouter = ({ children }) => {
507
const location = useLocation();
508
509
return (
510
<TransitionGroup>
511
<CSSTransition
512
key={location.pathname}
513
classNames="route"
514
timeout={300}
515
>
516
<Router location={location}>
517
{children}
518
</Router>
519
</CSSTransition>
520
</TransitionGroup>
521
);
522
};
523
```