Capítulo 36 de 57
TanStack Router coordinates per-route data loading via loader functions that run in parallel with route matching, backed by a built-in stale-while-revalidate cache keyed on pathname and loaderDeps, giving apps instant preloads and cached navigation without an external library.
beforeLoad serially, then loader (and component.preload) in parallel, with pendingComponent shown if slow and errorComponent shown on failure.loader parameters: receives abortController, cause (enter/preload/stay), context, deps (from loaderDeps), location, params, parentMatchPromise, preload (boolean), and route.loaderDeps: a deterministic, side-effect-free function that extracts specific validated search params to use as part of the loader's cache key; only include what the loader actually uses, including the whole search object causes unnecessary reloads.staleTime / defaultStaleTime: milliseconds a route's loaded data is considered fresh for navigation; defaults to 0 (immediately stale, revalidates in background on reuse). preloadStaleTime defaults to 30 seconds.gcTime / preloadGcTime: retention window (default 5 minutes) before unused cached data becomes eligible for garbage collection.staleReloadMode: 'background' (default, stale-while-revalidate, keeps showing old data while refetching) or 'blocking' (waits for the stale reload before rendering).shouldReload: boolean or function to opt a route out of automatic reloading beyond staleTime/loaderDeps changes, similar to Remix's shouldLoad.beforeLoad): beforeLoad runs before loader and can return an object merged into the route's context, useful for dependency injection (e.g. injecting a fetchPosts function) down the route tree.useLoaderData(): hook on the Route object (or via getRouteApi) to consume the loader's resolved data in a component.pendingComponent shows after pendingMs (default 1000ms) and stays at least pendingMinMs (default 500ms) to avoid flicker; errorComponent receives error and a reset() function, and can fall back to the router's default ErrorComponent.// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
validateSearch: z.object({ offset: z.number().int().nonnegative().catch(0) }),
loaderDeps: ({ search: { offset } }) => ({ offset }),
loader: async ({ deps: { offset } }) => fetchPosts({ offset }),
staleTime: 10_000,
})
loaderDeps and setting a custom staleTime.loaderDeps that the loader actually consumes, extraneous deps cause unnecessary reloads.staleTime: Infinity prevents reloads entirely; staleReloadMode: 'blocking' still reloads but waits for it, these are different mechanisms.loaderDeps to keep cache keys correct.router.invalidate({ sync: true }) to await invalidation of all matching loader generations; without sync, invalidation happens in the background.staleTime/gcTime machinery but with separate preloadStaleTime/preloadGcTime defaults.