or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-loading-hooks.mdindex.mdnavigation-components.mdnavigation-hooks.mdroute-configuration.mdrouter-components.mdrouter-creation.mdserver-side-rendering.mdutilities.md

utilities.mddocs/

0

# Utilities

1

2

Path manipulation utilities, route matching functions, and data response helpers for advanced routing scenarios.

3

4

## Capabilities

5

6

### Path Utilities

7

8

Functions for working with URL paths and route patterns.

9

10

```typescript { .api }

11

/**

12

* Generate path from pattern and parameters

13

* @param path - Path pattern with parameter placeholders

14

* @param params - Parameter values to substitute

15

* @returns Generated path string

16

*/

17

function generatePath<T extends Record<string, any>>(

18

path: string,

19

params?: T

20

): string;

21

```

22

23

**Usage Examples:**

24

25

```tsx

26

import { generatePath } from "react-router-dom";

27

28

// Basic parameter substitution

29

const userPath = generatePath("/users/:userId", { userId: "123" });

30

// Result: "/users/123"

31

32

// Multiple parameters

33

const postPath = generatePath("/users/:userId/posts/:postId", {

34

userId: "123",

35

postId: "abc"

36

});

37

// Result: "/users/123/posts/abc"

38

39

// Optional parameters

40

const searchPath = generatePath("/search/:category?", { category: "books" });

41

// Result: "/search/books"

42

43

const searchPathEmpty = generatePath("/search/:category?", {});

44

// Result: "/search"

45

46

// Complex patterns

47

const filePath = generatePath("/files/*", { "*": "documents/report.pdf" });

48

// Result: "/files/documents/report.pdf"

49

```

50

51

```typescript { .api }

52

/**

53

* Resolve relative path against current location

54

* @param to - Path to resolve (relative or absolute)

55

* @param fromPathname - Base pathname to resolve from

56

* @returns Resolved path object

57

*/

58

function resolvePath(to: To, fromPathname?: string): Path;

59

60

type To = string | Partial<Path>;

61

62

interface Path {

63

/** Resolved pathname */

64

pathname: string;

65

/** Query string */

66

search: string;

67

/** Hash fragment */

68

hash: string;

69

}

70

```

71

72

**Usage Examples:**

73

74

```tsx

75

import { resolvePath } from "react-router-dom";

76

77

// Resolve relative paths

78

const resolved = resolvePath("../settings", "/users/123/profile");

79

// Result: { pathname: "/users/123/settings", search: "", hash: "" }

80

81

// Resolve with search and hash

82

const resolvedComplex = resolvePath({

83

pathname: "./edit",

84

search: "?tab=general",

85

hash: "#form"

86

}, "/users/123");

87

// Result: { pathname: "/users/123/edit", search: "?tab=general", hash: "#form" }

88

89

// Absolute paths are returned as-is

90

const absolute = resolvePath("/admin/dashboard");

91

// Result: { pathname: "/admin/dashboard", search: "", hash: "" }

92

```

93

94

### Route Matching

95

96

Functions for matching routes against URL paths.

97

98

```typescript { .api }

99

/**

100

* Match path against a pattern

101

* @param pattern - Pattern to match against (string or PathPattern object)

102

* @param pathname - URL pathname to match

103

* @returns Match object with parameters, or null if no match

104

*/

105

function matchPath<T extends Record<string, any>>(

106

pattern: PathPattern<T> | string,

107

pathname: string

108

): PathMatch<T> | null;

109

110

interface PathPattern<T extends Record<string, any> = Record<string, any>> {

111

/** Path pattern with parameter placeholders */

112

path: string;

113

/** Enable case-sensitive matching */

114

caseSensitive?: boolean;

115

/** Match entire path (true) or just beginning (false) */

116

end?: boolean;

117

}

118

119

interface PathMatch<T extends Record<string, any> = Record<string, any>> {

120

/** Extracted route parameters */

121

params: T;

122

/** Matched portion of pathname */

123

pathname: string;

124

/** Base pathname for nested matches */

125

pathnameBase: string;

126

/** Pattern used for matching */

127

pattern: PathPattern<T>;

128

}

129

```

130

131

**Usage Examples:**

132

133

```tsx

134

import { matchPath } from "react-router-dom";

135

136

// Basic pattern matching

137

const match = matchPath("/users/:id", "/users/123");

138

// Result: {

139

// params: { id: "123" },

140

// pathname: "/users/123",

141

// pathnameBase: "/users/123",

142

// pattern: { path: "/users/:id", end: true }

143

// }

144

145

// Pattern with options

146

const optionalMatch = matchPath(

147

{ path: "/posts/:slug", caseSensitive: true, end: false },

148

"/posts/hello-world/comments"

149

);

150

// Result: {

151

// params: { slug: "hello-world" },

152

// pathname: "/posts/hello-world",

153

// pathnameBase: "/posts/hello-world",

154

// pattern: { path: "/posts/:slug", caseSensitive: true, end: false }

155

// }

156

157

// No match

158

const noMatch = matchPath("/users/:id", "/posts/123");

159

// Result: null

160

161

// Wildcard matching

162

const wildcardMatch = matchPath("/files/*", "/files/docs/readme.txt");

163

// Result: {

164

// params: { "*": "docs/readme.txt" },

165

// pathname: "/files/docs/readme.txt",

166

// pathnameBase: "/files",

167

// pattern: { path: "/files/*", end: true }

168

// }

169

```

170

171

```typescript { .api }

172

/**

173

* Match multiple routes against location

174

* @param routes - Array of route objects to match

175

* @param location - Location object or pathname string

176

* @param basename - Base URL for matching

177

* @returns Array of matched routes, or null if no matches

178

*/

179

function matchRoutes(

180

routes: RouteObject[],

181

location: Partial<Location> | string,

182

basename?: string

183

): RouteMatch[] | null;

184

185

interface RouteMatch<ParamKey extends string = string> {

186

/** Matched route parameters */

187

params: Params<ParamKey>;

188

/** Matched pathname portion */

189

pathname: string;

190

/** Base pathname for this match */

191

pathnameBase: string;

192

/** Route definition that matched */

193

route: RouteObject;

194

}

195

196

interface RouteObject {

197

path?: string;

198

index?: boolean;

199

children?: RouteObject[];

200

caseSensitive?: boolean;

201

id?: string;

202

loader?: LoaderFunction;

203

action?: ActionFunction;

204

element?: React.ReactNode | null;

205

errorElement?: React.ReactNode | null;

206

}

207

```

208

209

**Usage Examples:**

210

211

```tsx

212

import { matchRoutes } from "react-router-dom";

213

214

const routes = [

215

{

216

path: "/",

217

children: [

218

{ index: true, element: <Home /> },

219

{ path: "about", element: <About /> },

220

{

221

path: "users/:id",

222

element: <User />,

223

children: [

224

{ path: "profile", element: <Profile /> },

225

{ path: "settings", element: <Settings /> },

226

],

227

},

228

],

229

},

230

];

231

232

// Match nested route

233

const matches = matchRoutes(routes, "/users/123/profile");

234

// Result: [

235

// { params: {}, pathname: "/", pathnameBase: "/", route: routes[0] },

236

// { params: { id: "123" }, pathname: "/users/123", pathnameBase: "/users/123", route: userRoute },

237

// { params: { id: "123" }, pathname: "/users/123/profile", pathnameBase: "/users/123/profile", route: profileRoute }

238

// ]

239

240

// No matches

241

const noMatches = matchRoutes(routes, "/nonexistent");

242

// Result: null

243

```

244

245

### Data Response Helpers

246

247

Functions for creating responses in loaders and actions.

248

249

```typescript { .api }

250

/**

251

* Create response with JSON data

252

* @param data - Data to serialize as JSON

253

* @param init - Response initialization options

254

* @returns Response object with JSON data

255

*/

256

function data<T>(data: T, init?: number | ResponseInit): Response;

257

258

/**

259

* Create redirect response

260

* @param url - URL to redirect to

261

* @param init - Response status code or initialization options

262

* @returns Redirect response

263

*/

264

function redirect(url: string, init?: number | ResponseInit): Response;

265

266

/**

267

* Create document redirect response (full page reload)

268

* @param url - URL to redirect to

269

* @param init - Response initialization options

270

* @returns Document redirect response

271

*/

272

function redirectDocument(url: string, init?: ResponseInit): Response;

273

274

/**

275

* Create replace response (replaces current history entry)

276

* @param url - URL to replace with

277

* @param init - Response initialization options

278

* @returns Replace response

279

*/

280

function replace(url: string, init?: ResponseInit): Response;

281

```

282

283

**Usage Examples:**

284

285

```tsx

286

import { data, redirect, json } from "react-router-dom";

287

288

// Basic data response

289

export const loader = async () => {

290

const users = await fetchUsers();

291

return data(users);

292

};

293

294

// Data response with headers

295

export const loaderWithHeaders = async () => {

296

const posts = await fetchPosts();

297

return data(posts, {

298

headers: {

299

"Cache-Control": "public, max-age=300",

300

"X-Custom-Header": "value",

301

},

302

});

303

};

304

305

// Redirect responses

306

export const actionWithRedirect = async ({ request }) => {

307

const formData = await request.formData();

308

const user = await createUser(formData);

309

310

// Redirect to user profile

311

return redirect(`/users/${user.id}`);

312

};

313

314

// Conditional redirect

315

export const protectedLoader = async ({ request }) => {

316

const user = await getUser(request);

317

318

if (!user) {

319

return redirect("/login");

320

}

321

322

return data({ user });

323

};

324

325

// Error responses

326

export const loaderWithError = async ({ params }) => {

327

const user = await fetchUser(params.id);

328

329

if (!user) {

330

throw data("User not found", { status: 404 });

331

}

332

333

return data(user);

334

};

335

```

336

337

### Error Response Utilities

338

339

Functions for working with error responses.

340

341

```typescript { .api }

342

/**

343

* Check if error is a route error response

344

* @param error - Error object to check

345

* @returns True if error is a route error response

346

*/

347

function isRouteErrorResponse(error: any): error is ErrorResponse;

348

349

interface ErrorResponse {

350

/** HTTP status code */

351

status: number;

352

/** HTTP status text */

353

statusText: string;

354

/** Response data */

355

data: any;

356

/** Internal error flag */

357

internal: boolean;

358

}

359

```

360

361

**Usage Examples:**

362

363

```tsx

364

import { isRouteErrorResponse, useRouteError } from "react-router-dom";

365

366

function ErrorBoundary() {

367

const error = useRouteError();

368

369

if (isRouteErrorResponse(error)) {

370

return (

371

<div>

372

<h1>{error.status} {error.statusText}</h1>

373

<p>{error.data}</p>

374

</div>

375

);

376

}

377

378

// Handle other error types

379

return (

380

<div>

381

<h1>Something went wrong!</h1>

382

<p>{error?.message || "Unknown error"}</p>

383

</div>

384

);

385

}

386

```

387

388

### URL Search Parameters

389

390

Functions for working with URL search parameters.

391

392

```typescript { .api }

393

/**

394

* Create URLSearchParams instance from various input types

395

* @param init - Initial search parameters

396

* @returns URLSearchParams instance

397

*/

398

function createSearchParams(init?: URLSearchParamsInit): URLSearchParams;

399

400

type URLSearchParamsInit =

401

| string

402

| string[][]

403

| Record<string, string | string[]>

404

| URLSearchParams;

405

```

406

407

**Usage Examples:**

408

409

```tsx

410

import { createSearchParams } from "react-router-dom";

411

412

// From string

413

const params1 = createSearchParams("?q=react&category=library");

414

415

// From object

416

const params2 = createSearchParams({

417

q: "react",

418

category: "library",

419

sort: "popularity"

420

});

421

422

// From array of arrays

423

const params3 = createSearchParams([

424

["q", "react"],

425

["category", "library"],

426

["tag", "frontend"],

427

["tag", "javascript"] // Multiple values for same key

428

]);

429

430

// From existing URLSearchParams

431

const existingParams = new URLSearchParams("?page=1");

432

const params4 = createSearchParams(existingParams);

433

434

// Usage in loaders

435

export const loader = async ({ request }) => {

436

const url = new URL(request.url);

437

const searchParams = createSearchParams(url.search);

438

439

const query = searchParams.get("q") || "";

440

const page = parseInt(searchParams.get("page") || "1");

441

442

const results = await searchPosts(query, page);

443

return data({ results, query, page });

444

};

445

```

446

447

### Hook Utilities

448

449

Additional utility functions for working with router hooks.

450

451

```typescript { .api }

452

/**

453

* Create custom link click handler

454

* @param to - Navigation destination

455

* @param options - Navigation options

456

* @returns Click handler function

457

*/

458

function useLinkClickHandler<E extends Element = HTMLAnchorElement>(

459

to: To,

460

options?: {

461

target?: React.HTMLAttributeAnchorTarget;

462

replace?: boolean;

463

state?: any;

464

preventScrollReset?: boolean;

465

relative?: RelativeRoutingType;

466

}

467

): (event: React.MouseEvent<E, MouseEvent>) => void;

468

469

/**

470

* Create form submission handler

471

* @returns Submit function for programmatic form submission

472

*/

473

function useSubmit(): SubmitFunction;

474

475

type SubmitFunction = (

476

target: SubmitTarget,

477

options?: SubmitOptions

478

) => void;

479

480

type SubmitTarget =

481

| HTMLFormElement

482

| FormData

483

| URLSearchParams

484

| { [name: string]: string | File | (string | File)[] };

485

486

interface SubmitOptions {

487

action?: string;

488

method?: "get" | "post" | "put" | "patch" | "delete";

489

encType?: "application/x-www-form-urlencoded" | "multipart/form-data";

490

replace?: boolean;

491

preventScrollReset?: boolean;

492

relative?: RelativeRoutingType;

493

unstable_flushSync?: boolean;

494

}

495

```

496

497

**Usage Examples:**

498

499

```tsx

500

import { useLinkClickHandler, useSubmit } from "react-router-dom";

501

502

// Custom link component

503

function CustomLink({ to, children, ...props }) {

504

const handleClick = useLinkClickHandler(to, {

505

replace: props.replace,

506

state: props.state,

507

});

508

509

return (

510

<a {...props} onClick={handleClick}>

511

{children}

512

</a>

513

);

514

}

515

516

// Programmatic form submission

517

function SearchComponent() {

518

const submit = useSubmit();

519

520

const handleQuickSearch = (query: string) => {

521

submit(

522

{ q: query },

523

{ method: "get", action: "/search" }

524

);

525

};

526

527

const handleFileUpload = (file: File) => {

528

const formData = new FormData();

529

formData.append("file", file);

530

531

submit(formData, {

532

method: "post",

533

action: "/upload",

534

encType: "multipart/form-data",

535

});

536

};

537

538

return (

539

<div>

540

<button onClick={() => handleQuickSearch("react")}>

541

Quick Search

542

</button>

543

<input

544

type="file"

545

onChange={(e) => e.target.files?.[0] && handleFileUpload(e.target.files[0])}

546

/>

547

</div>

548

);

549

}

550

```

551

552

## Utility Patterns

553

554

### Path Generation

555

556

```tsx

557

// Generate paths for navigation

558

const routes = {

559

home: () => "/",

560

user: (id: string) => generatePath("/users/:id", { id }),

561

userPost: (userId: string, postId: string) =>

562

generatePath("/users/:userId/posts/:postId", { userId, postId }),

563

search: (query: string, filters?: Record<string, string>) => {

564

const params = createSearchParams({ q: query, ...filters });

565

return `/search?${params}`;

566

},

567

};

568

569

// Usage

570

<Link to={routes.user("123")}>User Profile</Link>

571

<Link to={routes.userPost("123", "abc")}>User Post</Link>

572

```

573

574

### Route Matching for Components

575

576

```tsx

577

// Conditional rendering based on route matching

578

function NavigationBreadcrumbs() {

579

const location = useLocation();

580

581

const userMatch = matchPath("/users/:id", location.pathname);

582

const postMatch = matchPath("/users/:id/posts/:postId", location.pathname);

583

584

const crumbs = ["Home"];

585

586

if (userMatch) {

587

crumbs.push(`User ${userMatch.params.id}`);

588

}

589

590

if (postMatch) {

591

crumbs.push(`Post ${postMatch.params.postId}`);

592

}

593

594

return (

595

<nav>

596

{crumbs.map((crumb, index) => (

597

<span key={index}>

598

{crumb}

599

{index < crumbs.length - 1 && " > "}

600

</span>

601

))}

602

</nav>

603

);

604

}

605

```

606

607

### Error Response Handling

608

609

```tsx

610

// Comprehensive error handling

611

export const loader = async ({ params }) => {

612

try {

613

const user = await fetchUser(params.id);

614

615

if (!user) {

616

throw data("User not found", {

617

status: 404,

618

statusText: "Not Found"

619

});

620

}

621

622

if (!user.isPublic && !await canViewUser(user.id)) {

623

throw data("Access denied", {

624

status: 403,

625

statusText: "Forbidden"

626

});

627

}

628

629

return data(user);

630

} catch (error) {

631

if (error instanceof Response) {

632

throw error; // Re-throw Response errors

633

}

634

635

console.error("Loader error:", error);

636

throw data("Failed to load user", {

637

status: 500,

638

statusText: "Internal Server Error"

639

});

640

}

641

};

642

```

643

644

## Navigation Constants

645

646

Constants representing different navigation and loading states.

647

648

```typescript { .api }

649

/**

650

* Constant representing idle navigation state

651

*/

652

const IDLE_NAVIGATION: Navigation;

653

654

/**

655

* Constant representing idle fetcher state

656

*/

657

const IDLE_FETCHER: Fetcher;

658

659

/**

660

* Constant representing idle blocker state

661

*/

662

const IDLE_BLOCKER: Blocker;

663

664

interface Navigation {

665

state: "idle" | "loading" | "submitting";

666

location?: Location;

667

formMethod?: FormMethod;

668

formAction?: string;

669

formEncType?: FormEncType;

670

formData?: FormData;

671

}

672

673

interface Fetcher {

674

state: "idle" | "loading" | "submitting";

675

data?: any;

676

formMethod?: FormMethod;

677

formAction?: string;

678

formEncType?: FormEncType;

679

formData?: FormData;

680

}

681

682

interface Blocker {

683

state: "unblocked" | "blocked" | "proceeding";

684

proceed?: () => void;

685

reset?: () => void;

686

location?: Location;

687

}

688

```

689

690

**Usage Examples:**

691

692

```tsx

693

import {

694

IDLE_NAVIGATION,

695

IDLE_FETCHER,

696

IDLE_BLOCKER,

697

useNavigation,

698

useFetcher,

699

useBlocker

700

} from "react-router-dom";

701

702

function NavigationStatus() {

703

const navigation = useNavigation();

704

const fetcher = useFetcher();

705

const blocker = useBlocker(false);

706

707

return (

708

<div>

709

<p>

710

Navigation: {navigation.state === IDLE_NAVIGATION.state ? "Idle" : "Active"}

711

</p>

712

<p>

713

Fetcher: {fetcher.state === IDLE_FETCHER.state ? "Idle" : "Active"}

714

</p>

715

<p>

716

Blocker: {blocker.state === IDLE_BLOCKER.state ? "Unblocked" : blocker.state}

717

</p>

718

</div>

719

);

720

}

721

722

// Checking for idle states in effects

723

function DataComponent() {

724

const navigation = useNavigation();

725

726

useEffect(() => {

727

if (navigation.state === IDLE_NAVIGATION.state) {

728

// Navigation completed, update UI

729

console.log("Navigation completed");

730

}

731

}, [navigation.state]);

732

733

return <div>Content</div>;

734

}

735

```