0
# Path & Search Utilities
1
2
Low-level utilities for path manipulation, search parameter handling, URL processing, and data validation for robust routing functionality.
3
4
## Capabilities
5
6
### Path Manipulation
7
8
Utilities for working with URL paths, including joining, cleaning, and normalization.
9
10
```typescript { .api }
11
/**
12
* Join multiple path segments into a single path
13
* @param paths - Array of path segments to join
14
* @returns Combined path string
15
*/
16
function joinPaths(paths: Array<string | undefined>): string;
17
18
/**
19
* Clean and normalize a path
20
* @param path - Path to clean
21
* @returns Cleaned path string
22
*/
23
function cleanPath(path: string): string;
24
25
/**
26
* Remove leading and trailing slashes from path
27
* @param path - Path to trim
28
* @returns Trimmed path string
29
*/
30
function trimPath(path: string): string;
31
32
/**
33
* Remove leading slashes from path
34
* @param path - Path to trim
35
* @returns Path without leading slashes
36
*/
37
function trimPathLeft(path: string): string;
38
39
/**
40
* Remove trailing slashes from path
41
* @param path - Path to trim
42
* @returns Path without trailing slashes
43
*/
44
function trimPathRight(path: string): string;
45
46
/**
47
* Resolve a relative path against a base path
48
* @param basepath - Base path
49
* @param base - Current base
50
* @param to - Target path
51
* @returns Resolved absolute path
52
*/
53
function resolvePath(basepath: string, base: string, to: string): string;
54
```
55
56
**Usage Examples:**
57
58
```typescript
59
import {
60
joinPaths,
61
cleanPath,
62
trimPath,
63
trimPathLeft,
64
trimPathRight,
65
resolvePath
66
} from "@tanstack/react-router";
67
68
// Join path segments
69
const fullPath = joinPaths(["/api", "users", undefined, "123"]);
70
// Result: "/api/users/123"
71
72
// Clean messy paths
73
const cleaned = cleanPath("//api///users//123//");
74
// Result: "/api/users/123"
75
76
// Trim slashes
77
const trimmed = trimPath("/path/to/resource/");
78
// Result: "path/to/resource"
79
80
const leftTrimmed = trimPathLeft("///path/to/resource");
81
// Result: "path/to/resource"
82
83
const rightTrimmed = trimPathRight("path/to/resource///");
84
// Result: "path/to/resource"
85
86
// Resolve relative paths
87
const resolved = resolvePath("/app", "/users", "../posts/123");
88
// Result: "/app/posts/123"
89
90
// Resolve from root
91
const absolute = resolvePath("", "/users/123", "../../admin");
92
// Result: "/admin"
93
```
94
95
### Path Parsing and Interpolation
96
97
Utilities for parsing paths and interpolating parameters.
98
99
```typescript { .api }
100
/**
101
* Parse a pathname into segments
102
* @param pathname - Pathname to parse
103
* @returns Array of path segments
104
*/
105
function parsePathname(pathname: string): Array<string>;
106
107
/**
108
* Interpolate path parameters into a path template
109
* @param path - Path template with parameter placeholders
110
* @param params - Parameters to interpolate
111
* @returns Path with parameters filled in
112
*/
113
function interpolatePath(path: string, params: Record<string, any>): string;
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import { parsePathname, interpolatePath } from "@tanstack/react-router";
120
121
// Parse pathname into segments
122
const segments = parsePathname("/users/123/posts/456");
123
// Result: ["users", "123", "posts", "456"]
124
125
const rootSegments = parsePathname("/");
126
// Result: []
127
128
// Interpolate parameters
129
const userPath = interpolatePath("/users/$userId/posts/$postId", {
130
userId: "123",
131
postId: "456",
132
});
133
// Result: "/users/123/posts/456"
134
135
// With optional parameters
136
const profilePath = interpolatePath("/profile/$userId?", {
137
userId: undefined,
138
});
139
// Result: "/profile"
140
141
// Complex interpolation
142
const complexPath = interpolatePath(
143
"/org/$orgId/project/$projectId/task/$taskId",
144
{
145
orgId: "acme",
146
projectId: "website",
147
taskId: "fix-bug-123",
148
}
149
);
150
// Result: "/org/acme/project/website/task/fix-bug-123"
151
```
152
153
### Search Parameter Processing
154
155
Utilities for parsing, stringifying, and manipulating URL search parameters.
156
157
```typescript { .api }
158
/**
159
* Default search parameter parser
160
* @param searchStr - Search string to parse
161
* @returns Parsed search object
162
*/
163
function defaultParseSearch(searchStr: string): Record<string, any>;
164
165
/**
166
* Default search parameter stringifier
167
* @param search - Search object to stringify
168
* @returns URL search string
169
*/
170
function defaultStringifySearch(search: Record<string, any>): string;
171
172
/**
173
* Create a custom search parser
174
* @param parser - Custom parser function
175
* @returns Search parser function
176
*/
177
function parseSearchWith<T>(
178
parser: (searchStr: string) => T
179
): (searchStr: string) => T;
180
181
/**
182
* Create a custom search stringifier
183
* @param stringify - Custom stringify function
184
* @returns Search stringifier function
185
*/
186
function stringifySearchWith<T>(
187
stringify: (search: T) => string
188
): (search: T) => string;
189
190
type SearchParser<T = any> = (searchStr: string) => T;
191
type SearchSerializer<T = any> = (search: T) => string;
192
```
193
194
**Usage Examples:**
195
196
```typescript
197
import {
198
defaultParseSearch,
199
defaultStringifySearch,
200
parseSearchWith,
201
stringifySearchWith
202
} from "@tanstack/react-router";
203
204
// Default parsing and stringifying
205
const searchObj = defaultParseSearch("?name=john&age=25&active=true");
206
// Result: { name: "john", age: "25", active: "true" }
207
208
const searchStr = defaultStringifySearch({
209
name: "jane",
210
age: 30,
211
tags: ["dev", "react"],
212
});
213
// Result: "name=jane&age=30&tags=dev&tags=react"
214
215
// Custom parser with type conversion
216
const typedParser = parseSearchWith((searchStr: string) => {
217
const params = new URLSearchParams(searchStr);
218
return {
219
page: Number(params.get("page")) || 1,
220
limit: Number(params.get("limit")) || 10,
221
sort: params.get("sort") || "created_at",
222
desc: params.get("desc") === "true",
223
tags: params.getAll("tags"),
224
};
225
});
226
227
const parsed = typedParser("?page=2&limit=20&desc=true&tags=react&tags=routing");
228
// Result: { page: 2, limit: 20, sort: "created_at", desc: true, tags: ["react", "routing"] }
229
230
// Custom stringifier
231
const typedStringifier = stringifySearchWith((search: {
232
page: number;
233
limit: number;
234
filters?: Record<string, any>;
235
}) => {
236
const params = new URLSearchParams();
237
params.set("page", search.page.toString());
238
params.set("limit", search.limit.toString());
239
240
if (search.filters) {
241
Object.entries(search.filters).forEach(([key, value]) => {
242
if (Array.isArray(value)) {
243
value.forEach(v => params.append(key, String(v)));
244
} else {
245
params.set(key, String(value));
246
}
247
});
248
}
249
250
return params.toString();
251
});
252
```
253
254
### Search Parameter Filtering
255
256
Utilities for filtering and manipulating search parameters.
257
258
```typescript { .api }
259
/**
260
* Retain only specific search parameters
261
* @param search - Search object to filter
262
* @param retain - Array of keys to retain
263
* @returns Filtered search object
264
*/
265
function retainSearchParams<T>(
266
search: T,
267
retain: Array<string | number>
268
): Partial<T>;
269
270
/**
271
* Remove specific search parameters
272
* @param search - Search object to filter
273
* @param strip - Array of keys to remove
274
* @returns Filtered search object
275
*/
276
function stripSearchParams<T>(
277
search: T,
278
strip: Array<string | number>
279
): Partial<T>;
280
```
281
282
**Usage Examples:**
283
284
```typescript
285
import { retainSearchParams, stripSearchParams } from "@tanstack/react-router";
286
287
const searchParams = {
288
page: 1,
289
limit: 10,
290
sort: "name",
291
filter: "active",
292
debug: true,
293
internal: "secret",
294
};
295
296
// Retain only specific params
297
const publicParams = retainSearchParams(searchParams, ["page", "limit", "sort", "filter"]);
298
// Result: { page: 1, limit: 10, sort: "name", filter: "active" }
299
300
// Strip sensitive params
301
const cleanParams = stripSearchParams(searchParams, ["debug", "internal"]);
302
// Result: { page: 1, limit: 10, sort: "name", filter: "active" }
303
304
// Use in navigation
305
function NavigateWithCleanParams() {
306
const currentSearch = useSearch();
307
const navigate = useNavigate();
308
309
const navigateToNextPage = () => {
310
const cleanSearch = stripSearchParams(currentSearch, ["debug", "internal"]);
311
navigate({
312
search: {
313
...cleanSearch,
314
page: (cleanSearch.page || 1) + 1,
315
},
316
});
317
};
318
319
return <button onClick={navigateToNextPage}>Next Page</button>;
320
}
321
```
322
323
### Deep Comparison Utilities
324
325
Utilities for deep equality checking and structural sharing.
326
327
```typescript { .api }
328
/**
329
* Deep equality comparison
330
* @param a - First value to compare
331
* @param b - Second value to compare
332
* @param opts - Comparison options
333
* @returns Whether values are deeply equal
334
*/
335
function deepEqual(
336
a: any,
337
b: any,
338
opts?: {
339
partial?: boolean;
340
ignoreUndefined?: boolean;
341
}
342
): boolean;
343
344
/**
345
* Deep equality replacement for structural sharing
346
* @param prev - Previous value
347
* @param next - Next value
348
* @returns Previous value if equal, next value if different
349
*/
350
function replaceEqualDeep<T>(prev: T, next: T): T;
351
352
/**
353
* Check if value is a plain object
354
* @param obj - Value to check
355
* @returns Whether value is a plain object
356
*/
357
function isPlainObject(obj: any): boolean;
358
359
/**
360
* Check if value is a plain array
361
* @param obj - Value to check
362
* @returns Whether value is a plain array
363
*/
364
function isPlainArray(obj: any): boolean;
365
```
366
367
**Usage Examples:**
368
369
```typescript
370
import {
371
deepEqual,
372
replaceEqualDeep,
373
isPlainObject,
374
isPlainArray
375
} from "@tanstack/react-router";
376
377
// Deep equality comparison
378
const obj1 = { a: 1, b: { c: 2, d: [3, 4] } };
379
const obj2 = { a: 1, b: { c: 2, d: [3, 4] } };
380
const obj3 = { a: 1, b: { c: 2, d: [3, 5] } };
381
382
console.log(deepEqual(obj1, obj2)); // true
383
console.log(deepEqual(obj1, obj3)); // false
384
385
// Partial comparison
386
const partial = { a: 1, b: { c: 2 } };
387
const full = { a: 1, b: { c: 2, d: 3 }, e: 4 };
388
console.log(deepEqual(partial, full, { partial: true })); // true
389
390
// Structural sharing for performance
391
const prevState = { users: [1, 2, 3], posts: [4, 5, 6] };
392
const nextState = { users: [1, 2, 3], posts: [4, 5, 7] };
393
394
const optimized = replaceEqualDeep(prevState, nextState);
395
// optimized.users === prevState.users (same reference)
396
// optimized.posts !== prevState.posts (different reference)
397
398
// Type checking
399
console.log(isPlainObject({})); // true
400
console.log(isPlainObject([])); // false
401
console.log(isPlainObject(new Date())); // false
402
403
console.log(isPlainArray([])); // true
404
console.log(isPlainArray({})); // false
405
console.log(isPlainArray("string")); // false
406
```
407
408
### Functional Update Utilities
409
410
Utilities for applying functional updates to data.
411
412
```typescript { .api }
413
/**
414
* Apply a functional update to a value
415
* @param updater - Update function or new value
416
* @param previous - Previous value
417
* @returns Updated value
418
*/
419
function functionalUpdate<T>(
420
updater: T | ((prev: T) => T),
421
previous: T
422
): T;
423
```
424
425
**Usage Examples:**
426
427
```typescript
428
import { functionalUpdate } from "@tanstack/react-router";
429
430
// Simple value update
431
const newValue = functionalUpdate("new", "old");
432
// Result: "new"
433
434
// Functional update
435
const newObj = functionalUpdate(
436
(prev) => ({ ...prev, updated: true }),
437
{ id: 1, name: "test" }
438
);
439
// Result: { id: 1, name: "test", updated: true }
440
441
// Use in search parameter updates
442
function SearchUpdater() {
443
const search = useSearch();
444
const navigate = useNavigate();
445
446
const updateSearch = (updater: (prev: any) => any) => {
447
const newSearch = functionalUpdate(updater, search);
448
navigate({ search: newSearch });
449
};
450
451
return (
452
<div>
453
<button
454
onClick={() =>
455
updateSearch((prev) => ({ ...prev, page: (prev.page || 1) + 1 }))
456
}
457
>
458
Next Page
459
</button>
460
<button
461
onClick={() =>
462
updateSearch((prev) => ({ ...prev, sort: prev.sort === "asc" ? "desc" : "asc" }))
463
}
464
>
465
Toggle Sort
466
</button>
467
</div>
468
);
469
}
470
```
471
472
### URL Construction Utilities
473
474
Utilities for constructing and manipulating URLs.
475
476
```typescript { .api }
477
/**
478
* Root route identifier constant
479
*/
480
const rootRouteId: "__root__";
481
482
/**
483
* Check if value matches a route match object
484
* @param obj - Object to check
485
* @returns Whether object is a route match
486
*/
487
function isMatch(obj: any): obj is RouteMatch;
488
```
489
490
**Usage Examples:**
491
492
```typescript
493
import { rootRouteId, isMatch } from "@tanstack/react-router";
494
495
// Root route identification
496
const isRootRoute = (routeId: string) => routeId === rootRouteId;
497
498
// Route match type checking
499
function processMatches(matches: unknown[]) {
500
const validMatches = matches.filter(isMatch);
501
502
return validMatches.map(match => ({
503
id: match.id,
504
pathname: match.pathname,
505
params: match.params,
506
}));
507
}
508
509
// Use in route tree construction
510
const routeTree = rootRoute.addChildren([
511
homeRoute,
512
aboutRoute,
513
postsRoute.addChildren([
514
postDetailRoute,
515
newPostRoute,
516
]),
517
]);
518
```
519
520
### Route Rendering Utilities
521
522
Utilities for handling route rendering, particularly for not found scenarios.
523
524
```typescript { .api }
525
/**
526
* Renders the appropriate not found component for a route
527
* @param router - Router instance
528
* @param route - Route that triggered not found
529
* @param data - Optional data to pass to not found component
530
* @returns JSX element with not found component
531
*/
532
function renderRouteNotFound(
533
router: AnyRouter,
534
route: AnyRoute,
535
data: any
536
): JSX.Element;
537
```
538
539
**Usage Examples:**
540
541
```typescript
542
import { renderRouteNotFound } from "@tanstack/react-router";
543
544
// Manual not found rendering (typically handled internally)
545
function CustomRouteRenderer({ router, route, error }: {
546
router: AnyRouter;
547
route: AnyRoute;
548
error: any;
549
}) {
550
// Handle not found scenarios
551
if (error?.status === 404) {
552
return renderRouteNotFound(router, route, error);
553
}
554
555
// Handle other rendering scenarios
556
return <route.component />;
557
}
558
```
559
560
## Types
561
562
### Path Types
563
564
```typescript { .api }
565
type Segment = string;
566
567
type RemoveTrailingSlashes<T extends string> = T extends `${infer R}/`
568
? RemoveTrailingSlashes<R>
569
: T;
570
571
type RemoveLeadingSlashes<T extends string> = T extends `/${infer R}`
572
? RemoveLeadingSlashes<R>
573
: T;
574
575
type TrimPath<T extends string> = RemoveTrailingSlashes<RemoveLeadingSlashes<T>>;
576
type TrimPathLeft<T extends string> = RemoveLeadingSlashes<T>;
577
type TrimPathRight<T extends string> = RemoveTrailingSlashes<T>;
578
```
579
580
### Search Types
581
582
```typescript { .api }
583
interface SearchFilter {
584
key: string;
585
value: any;
586
operator: "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains";
587
}
588
589
type SearchSchemaInput = Record<string, any>;
590
591
interface ParsedLocation {
592
pathname: string;
593
search: Record<string, any>;
594
searchStr: string;
595
hash: string;
596
href: string;
597
state?: any;
598
}
599
```
600
601
### Utility Types
602
603
```typescript { .api }
604
type Constrain<T, U> = T extends U ? T : U;
605
606
type Expand<T> = T extends (...args: infer A) => infer R
607
? (...args: Expand<A>) => Expand<R>
608
: T extends object
609
? T extends infer O
610
? { [K in keyof O]: Expand<O[K]> }
611
: never
612
: T;
613
614
type Assign<T, U> = {
615
[K in keyof T]: K extends keyof U ? U[K] : T[K];
616
} & {
617
[K in keyof U]: U[K];
618
};
619
620
type MergeAll<T extends readonly unknown[]> = T extends readonly [
621
infer H,
622
...infer Tail
623
]
624
? H & MergeAll<Tail>
625
: {};
626
627
type IntersectAssign<T, U> = T & U;
628
```