Capítulo 40 de 57

Chapter 40: Preloading

Core Idea

TanStack Router can preload a route's chunk, beforeLoad, and loader before the user navigates to it, using intent (hover/touch), viewport visibility, or render-time strategies, with separate freshness (preloadStaleTime) and retention (preloadGcTime) settings from normal navigation.

Key Concepts

  • Intent preloading: triggers on hover/touchstart of a <Link>, preloading the destination route; the most common strategy for likely-next navigations.
  • Viewport preloading: uses the Intersection Observer API to preload a <Link>'s destination once it's visible in the viewport, useful for below-the-fold links.
  • Render preloading: preloads a <Link>'s destination as soon as it's rendered in the DOM, for routes that are almost always needed.
  • defaultPreload router option: set to 'intent' (most common), 'viewport', or 'render' to enable that strategy app-wide; overridable per-<Link> via the preload prop.
  • defaultPreloadDelay / preloadDelay: milliseconds before an intent/viewport preload starts (default 50ms); pending preloads cancel if hover/focus ends or the link leaves viewport; touch intent preloads immediately.
  • preloadStaleTime / defaultPreloadStaleTime: freshness window for preloaded loader data, default 30 seconds; separate from staleTime used for navigation.
  • preloadGcTime / defaultPreloadGcTime: retention window for unused preloaded data before eligible pruning, default 5 minutes; independent of freshness.
  • Speculative lane: a preload runs its own beforeLoad chain and is never directly promoted into router state; navigation always runs its own beforeLoad again but can reuse a preload's settled loader data or in-flight loader work.
  • router.preloadRoute(options): manually trigger a preload; returns the speculative match lane (or undefined if cancelled), letting you inspect for status === 'error' or 'notFound'.
  • router.loadRouteChunk(route): preload only a route's JS chunk (not its loader/beforeLoad), returning a promise that resolves when loaded.

Code Examples

const router = createRouter({
  routeTree,
  defaultPreload: 'intent',
  defaultPreloadDelay: 100,
})
  • What it demonstrates: enabling intent-based preloading app-wide with a custom delay.
// src/routes/posts.$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => fetchPost(params.postId),
  preloadStaleTime: 10_000, // reload preloaded data older than 10s
})
  • What it demonstrates: per-route override of preload freshness.

Key Takeaways

  1. defaultPreload: 'intent' is the simplest high-value win for perceived navigation speed in most apps.
  2. When integrating an external cache (TanStack Query), set defaultPreloadStaleTime: 0 so the external library's own freshness logic takes over.
  3. preload: false on a route still runs beforeLoad speculatively but skips the loader, navigation will still run both.
  4. Preloaded data retention (preloadGcTime) and freshness (preloadStaleTime) are independent settings, don't conflate them.

Connects To

  • Ch 36: Data Loading, shares the same loader mechanics and cache, just with separate preload-specific defaults.
  • Ch 34: Code Splitting, loadRouteChunk preloads only the split JS chunk, complementary to data preloading.
  • Ch 38: External Data Loading, for coordinating preload freshness with an external cache's own staleTime.