or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-loading.mderror-handling.mdindex.mdnavigation-links.mdpath-search-utils.mdreact-components.mdreact-hooks.mdroute-definition.mdrouter-creation.mdssr.md

data-loading.mddocs/

0

# Data Loading & Caching

1

2

Built-in data loading system with loaders, caching, invalidation, promise handling, and deferred data streaming for efficient data management.

3

4

## Capabilities

5

6

### Deferred Promises

7

8

Create deferred promises for streaming data loading and progressive rendering.

9

10

```typescript { .api }

11

/**

12

* Create a deferred promise for streaming data

13

* @param promise - Promise to defer

14

* @returns Deferred promise wrapper for streaming

15

*/

16

function defer<T>(promise: Promise<T>): DeferredPromise<T>;

17

18

interface DeferredPromise<T> extends Promise<T> {

19

/** Deferred promise state marker */

20

[TSR_DEFERRED_PROMISE]: DeferredPromiseState<T>;

21

/** Access to deferred state */

22

__deferredState: DeferredPromiseState<T>;

23

}

24

25

interface DeferredPromiseState<T> {

26

/** Current status of the promise */

27

status: "pending" | "success" | "error";

28

/** Resolved data if successful */

29

data?: T;

30

/** Error if promise rejected */

31

error?: any;

32

}

33

34

/** Symbol key for deferred promise state */

35

declare const TSR_DEFERRED_PROMISE: unique symbol;

36

```

37

38

**Usage Examples:**

39

40

```typescript

41

import { defer } from "@tanstack/react-router";

42

43

// In route loader - defer slow data

44

const Route = createRoute({

45

path: "/dashboard",

46

loader: async () => {

47

// Load fast data immediately

48

const user = await fetchUser();

49

50

// Defer slow data for streaming

51

const analytics = defer(fetchAnalytics());

52

const reports = defer(fetchReports());

53

54

return {

55

user, // Available immediately

56

analytics, // Streams in when ready

57

reports, // Streams in when ready

58

};

59

},

60

});

61

62

// In component - handle deferred data

63

function Dashboard() {

64

const { user, analytics, reports } = Route.useLoaderData();

65

66

return (

67

<div>

68

<h1>Welcome, {user.name}</h1>

69

70

<Suspense fallback={<div>Loading analytics...</div>}>

71

<Await promise={analytics}>

72

{(data) => <AnalyticsChart data={data} />}

73

</Await>

74

</Suspense>

75

76

<Suspense fallback={<div>Loading reports...</div>}>

77

<Await promise={reports}>

78

{(data) => <ReportsList reports={data} />}

79

</Await>

80

</Suspense>

81

</div>

82

);

83

}

84

```

85

86

### Controlled Promises

87

88

Create promises with external control for advanced async patterns.

89

90

```typescript { .api }

91

/**

92

* Create a controllable promise with external resolve/reject

93

* @returns Promise with control methods

94

*/

95

function createControlledPromise<T>(): ControlledPromise<T>;

96

97

interface ControlledPromise<T> extends Promise<T> {

98

/** Resolve the promise */

99

resolve: (value: T | PromiseLike<T>) => void;

100

/** Reject the promise */

101

reject: (reason?: any) => void;

102

/** Current status */

103

status: "pending" | "resolved" | "rejected";

104

}

105

```

106

107

**Usage Examples:**

108

109

```typescript

110

import { createControlledPromise } from "@tanstack/react-router";

111

112

// Custom async operation with external control

113

function createAsyncOperation() {

114

const { promise, resolve, reject } = createControlledPromise<string>();

115

116

// Simulate async operation

117

setTimeout(() => {

118

if (Math.random() > 0.5) {

119

resolve("Success!");

120

} else {

121

reject(new Error("Failed"));

122

}

123

}, 1000);

124

125

return {

126

promise,

127

cancel: () => reject(new Error("Cancelled")),

128

forceSuccess: () => resolve("Forced success"),

129

};

130

}

131

132

// In route loader

133

const Route = createRoute({

134

path: "/async-demo",

135

loader: () => {

136

const operation = createAsyncOperation();

137

return { result: defer(operation.promise) };

138

},

139

});

140

```

141

142

### Loader Data Hooks

143

144

Hooks for accessing loader data with type safety and selection.

145

146

```typescript { .api }

147

/**

148

* Access loader data from route with type safety

149

* @param opts - Loader data access options

150

* @returns Loader data or selected subset

151

*/

152

function useLoaderData<

153

TRouter extends AnyRouter = RegisteredRouter,

154

TFrom extends RoutePaths<TRouter> = "/",

155

TStrict extends boolean = true,

156

TSelected = ResolveLoaderData<TRouter, TFrom>,

157

TStructuralSharing extends boolean = true

158

>(

159

opts?: {

160

from?: TFrom;

161

strict?: TStrict;

162

select?: (data: ResolveLoaderData<TRouter, TFrom>) => TSelected;

163

structuralSharing?: TStructuralSharing;

164

}

165

): UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>;

166

167

/**

168

* Access loader dependencies

169

* @param opts - Loader deps access options

170

* @returns Loader dependencies

171

*/

172

function useLoaderDeps<

173

TRouter extends AnyRouter = RegisteredRouter,

174

TFrom extends RoutePaths<TRouter> = "/",

175

TStrict extends boolean = true,

176

TSelected = ResolveLoaderDeps<TRouter, TFrom>,

177

TStructuralSharing extends boolean = true

178

>(

179

opts?: {

180

from?: TFrom;

181

strict?: TStrict;

182

select?: (deps: ResolveLoaderDeps<TRouter, TFrom>) => TSelected;

183

structuralSharing?: TStructuralSharing;

184

}

185

): UseLoaderDepsResult<TRouter, TFrom, TStrict, TSelected>;

186

```

187

188

**Usage Examples:**

189

190

```typescript

191

import { useLoaderData, useLoaderDeps } from "@tanstack/react-router";

192

193

// Access loader data in component

194

function PostDetail() {

195

// Get all loader data

196

const { post, comments, relatedPosts } = useLoaderData({

197

from: "/posts/$postId",

198

});

199

200

// Select specific data

201

const postTitle = useLoaderData({

202

from: "/posts/$postId",

203

select: (data) => data.post.title,

204

});

205

206

// Access loader deps for cache invalidation

207

const deps = useLoaderDeps({

208

from: "/posts/$postId",

209

});

210

211

return (

212

<div>

213

<h1>{postTitle}</h1>

214

<div>{post.content}</div>

215

<CommentsList comments={comments} />

216

<RelatedPosts posts={relatedPosts} />

217

</div>

218

);

219

}

220

```

221

222

### Awaited Data Hook

223

224

Hook for handling deferred promises with suspense integration.

225

226

```typescript { .api }

227

/**

228

* Handle deferred promises with suspense integration

229

* @param options - Await options

230

* @returns Tuple of resolved data and promise state

231

*/

232

function useAwaited<T>(options: AwaitOptions<T>): [T, DeferredPromise<T>];

233

234

interface AwaitOptions<T> {

235

/** Promise to await */

236

promise: Promise<T> | DeferredPromise<T>;

237

}

238

```

239

240

**Usage Examples:**

241

242

```typescript

243

import { useAwaited, defer } from "@tanstack/react-router";

244

245

function StreamingDataComponent() {

246

const { deferredAnalytics } = useLoaderData();

247

248

try {

249

// This will suspend until promise resolves

250

const [analytics, promise] = useAwaited({ promise: deferredAnalytics });

251

252

return (

253

<div>

254

<h2>Analytics (Status: {promise.__deferredState.status})</h2>

255

<AnalyticsChart data={analytics} />

256

</div>

257

);

258

} catch (promise) {

259

// Suspense boundary will catch and show fallback

260

throw promise;

261

}

262

}

263

264

// Alternative usage with error handling

265

function SafeStreamingComponent() {

266

const { deferredData } = useLoaderData();

267

268

try {

269

const [data, promise] = useAwaited({ promise: deferredData });

270

271

if (promise.__deferredState.status === "error") {

272

return <div>Error loading data: {promise.__deferredState.error.message}</div>;

273

}

274

275

return <DataDisplay data={data} />;

276

} catch (promise) {

277

// Still loading

278

return <div>Loading...</div>;

279

}

280

}

281

```

282

283

### Cache Invalidation

284

285

Utilities for invalidating cached route data and triggering reloads.

286

287

```typescript { .api }

288

/**

289

* Router invalidation method

290

* Invalidates all route matches and triggers reload

291

*/

292

interface Router {

293

/**

294

* Invalidate all route data and reload

295

* @returns Promise that resolves when invalidation completes

296

*/

297

invalidate(): Promise<void>;

298

299

/**

300

* Preload a route's data

301

* @param options - Navigation options for route to preload

302

* @returns Promise that resolves when preloading completes

303

*/

304

preloadRoute<TFrom extends RoutePaths<TRouteTree> = "/">(

305

options: NavigateOptions<TRouteTree, TFrom>

306

): Promise<void>;

307

}

308

```

309

310

**Usage Examples:**

311

312

```typescript

313

import { useRouter } from "@tanstack/react-router";

314

315

function DataManagement() {

316

const router = useRouter();

317

318

const handleRefreshData = async () => {

319

// Invalidate all cached data and reload

320

await router.invalidate();

321

};

322

323

const handlePreloadProfile = async () => {

324

// Preload profile data

325

await router.preloadRoute({ to: "/profile" });

326

};

327

328

return (

329

<div>

330

<button onClick={handleRefreshData}>Refresh All Data</button>

331

<button onClick={handlePreloadProfile}>Preload Profile</button>

332

</div>

333

);

334

}

335

```

336

337

### Route Loader Functions

338

339

Types and utilities for defining route data loaders.

340

341

```typescript { .api }

342

/**

343

* Route loader function type

344

*/

345

type RouteLoaderFn<TRoute extends AnyRoute = AnyRoute> = (

346

context: LoaderFnContext<TRoute>

347

) => any | Promise<any>;

348

349

interface LoaderFnContext<TRoute extends AnyRoute = AnyRoute> {

350

/** Route parameters */

351

params: ResolveParams<TRoute>;

352

/** Search parameters */

353

search: InferFullSearchSchema<TRoute>;

354

/** Route context from beforeLoad */

355

context: RouteContext<TRoute>;

356

/** Current location */

357

location: ParsedLocation;

358

/** Abort signal for cancellation */

359

signal: AbortSignal;

360

/** Preload flag */

361

preload?: boolean;

362

}

363

364

/**

365

* Loader data resolution types

366

*/

367

type ResolveLoaderData<TRouter extends AnyRouter, TFrom extends RoutePaths<TRouter>> =

368

RouteById<TRouter, TFrom>["loaderData"];

369

370

type ResolveLoaderDeps<TRouter extends AnyRouter, TFrom extends RoutePaths<TRouter>> =

371

RouteById<TRouter, TFrom>["loaderDeps"];

372

```

373

374

**Usage Examples:**

375

376

```typescript

377

// Advanced loader with error handling and caching

378

const Route = createRoute({

379

path: "/api/data/$id",

380

loader: async ({ params, search, context, signal, preload }: LoaderFnContext) => {

381

// Check if this is a preload

382

if (preload) {

383

// Maybe return cached data for preloads

384

return getCachedData(params.id);

385

}

386

387

// Use abort signal for cleanup

388

const controller = new AbortController();

389

signal.addEventListener("abort", () => controller.abort());

390

391

try {

392

// Load multiple data sources

393

const [item, metadata] = await Promise.all([

394

fetchItem(params.id, { signal: controller.signal }),

395

fetchMetadata(params.id, { signal: controller.signal }),

396

]);

397

398

// Apply search filters

399

const filteredData = applySearchFilters(item, search);

400

401

// Use context for authorization

402

const authorizedData = await authorizeData(filteredData, context.user);

403

404

return {

405

item: authorizedData,

406

metadata,

407

loadedAt: Date.now(),

408

};

409

} catch (error) {

410

if (error.name === "AbortError") {

411

throw new Error("Request cancelled");

412

}

413

throw error;

414

}

415

},

416

// Configure caching

417

staleTime: 5 * 60 * 1000, // 5 minutes

418

gcTime: 30 * 60 * 1000, // 30 minutes

419

});

420

```

421

422

### Data Transformation

423

424

Utilities for transforming and updating loader data.

425

426

```typescript { .api }

427

/**

428

* Apply functional updates to data

429

* @param updater - Update function or new value

430

* @param previous - Previous value

431

* @returns Updated value

432

*/

433

function functionalUpdate<T>(

434

updater: T | ((prev: T) => T),

435

previous: T

436

): T;

437

438

/**

439

* Deep equality replacement for structural sharing

440

* @param prev - Previous value

441

* @param next - Next value

442

* @returns Previous if equal, next if different

443

*/

444

function replaceEqualDeep<T>(prev: T, next: T): T;

445

```

446

447

**Usage Examples:**

448

449

```typescript

450

import { functionalUpdate, replaceEqualDeep } from "@tanstack/react-router";

451

452

// Functional updates in loader

453

const Route = createRoute({

454

path: "/settings",

455

loader: async ({ context }) => {

456

const settings = await fetchSettings();

457

458

// Apply functional update based on context

459

const updatedSettings = functionalUpdate(

460

(prev) => ({

461

...prev,

462

theme: context.user.preferredTheme,

463

}),

464

settings

465

);

466

467

return { settings: updatedSettings };

468

},

469

});

470

471

// Structural sharing for performance

472

function useOptimizedData() {

473

const data = useLoaderData();

474

475

// Only re-render if data actually changed

476

const optimizedData = useMemo(() =>

477

replaceEqualDeep(previousData.current, data),

478

[data]

479

);

480

481

return optimizedData;

482

}

483

```

484

485

## Types

486

487

### Loader Types

488

489

```typescript { .api }

490

interface LoaderContext<TRoute extends AnyRoute = AnyRoute> {

491

params: ResolveParams<TRoute>;

492

search: InferFullSearchSchema<TRoute>;

493

context: RouteContext<TRoute>;

494

location: ParsedLocation;

495

signal: AbortSignal;

496

preload?: boolean;

497

}

498

499

type LoaderData<TRoute extends AnyRoute = AnyRoute> = TRoute extends {

500

loader: infer TLoader;

501

}

502

? TLoader extends (...args: any[]) => infer TReturn

503

? TReturn extends Promise<infer TData>

504

? TData

505

: TReturn

506

: never

507

: {};

508

```

509

510

### Promise State Types

511

512

```typescript { .api }

513

interface DeferredPromiseState<T> {

514

status: "pending" | "success" | "error";

515

data?: T;

516

error?: any;

517

}

518

519

type ControllablePromise<T> = Promise<T> & {

520

resolve: (value: T | PromiseLike<T>) => void;

521

reject: (reason?: any) => void;

522

status: "pending" | "resolved" | "rejected";

523

};

524

```

525

526

### Cache Configuration Types

527

528

```typescript { .api }

529

interface CacheOptions {

530

/** Time until data becomes stale */

531

staleTime?: number;

532

/** Time until data is garbage collected */

533

gcTime?: number;

534

/** Whether route should reload on focus */

535

shouldReload?: boolean | ((match: RouteMatch) => boolean);

536

}

537

```