Preloading
Key points (written by a model)
- TanStack Router supports three preloading strategies: intent, viewport visibility, and render, each triggering on a different Link event.
- Preloaded loader results stay fresh for 30 seconds by default and unused entries are retained for 5 minutes before pruning.
- Intent focus/hover and viewport preloading wait 50ms by default, and touch intent preloads immediately; set defaultPreloadDelay or preloadDelay to change it.
- Freshness and retention are separate: preloadStaleTime controls reuse without another loader call, while preloadGcTime makes unused results eligible for pruning at the next reconciliation.
- Set defaultPreloadStaleTime or preloadStaleTime to 0 so an external cache like React Query decides freshness while retention still follows preloadGcTime.
Model-written
contextclaimmethodsetupresultanalysislimitation
Preloading in TanStack Router is a way to load a route before the user actually navigates to it. This is useful for routes that are likely to be visited by the user next. For example, if you have a list of posts and the user is likely to click on one of them, you can preload the post route so that it's ready to go when the user clicks on it.Preloading in TanStack Router is a way to load a route before the user actually navigates to it. This is useful for routes that are likely to be visited by the user next.Preloading loads a route before the user navigates to it It readies likely-next routes, like a post page, so they are ready on clickPreloading loads a route before the user navigates to itPreloading
Supported Preloading Strategies
Intent, viewport and render strategies preload routes
- Intent
- Preloading by "intent" works by using hover and touch start events on
<Link>components to preload the dependencies for the destination route.Preloading by "intent" works by using hover and touch start events on<Link>components to preload the dependencies for the destination route.Intent preloading uses hover and touch start events on Link Those events signal the user intends to visit the destination route nextIntent preloading uses hover and touch start events on LinkIntent preloading - This strategy is useful for preloading routes that the user is likely to visit next.This strategy is useful for preloading routes that the user is likely to visit next.Intent preloading suits routes the user is likely to visit next It trades a small hover-time load for an instant navigation on clickIntent preloading suits routes the user is likely to visit nextIntent preloading
- Viewport Visibility
- Preloading by "viewport" works by using the Intersection Observer API to preload the dependencies for the destination route when the
<Link>component is in the viewport.Preloading by "viewport" works by using the Intersection Observer API to preload the dependencies for the destination route when the<Link>component is in the viewport.Viewport preloading uses the Intersection Observer API on Link It loads destination dependencies once the Link component enters the viewportViewport preloading uses the Intersection Observer API on LinkViewport preloading - This strategy is useful for preloading routes that are below the fold or off-screen.This strategy is useful for preloading routes that are below the fold or off-screen.Viewport preloading suits routes below the fold or off-screen Those links become visible as the user scrolls toward themViewport preloading suits routes below the fold or off-screenViewport preloading
- Render
- Preloading by "render" works by preloading the dependencies for the destination route as soon as the
<Link>component is rendered in the DOM.Preloading by "render" works by preloading the dependencies for the destination route as soon as the<Link>component is rendered in the DOM.Render preloading loads dependencies as soon as Link renders in the DOM No user interaction is needed, so dependencies start loading immediatelyRender preloading loads dependencies as soon as Link renders in the DOMRender preloading - This strategy is useful for preloading routes that are always needed.This strategy is useful for preloading routes that are always needed.Render preloading suits routes that are always needed Routes certain to be visited are worth loading before any interactionRender preloading suits routes that are always neededRender preloading
How long does preloaded data stay in memory?
Preloaded data stays fresh 30 seconds and retained 5 minutes
Successful preloaded loader results can enter the router's in-memory cache with two independent policies:Successful preloaded loader results can enter the router's in-memory cache with two independent policies:Preloaded loader results enter the in-memory cache under two policies Freshness and unused retention are configured independentlyPreloaded loader results enter the in-memory cache under two policiescache policies
- Freshness defaults to 30 seconds. Configure it with
defaultPreloadStaleTimeor a route'spreloadStaleTime.Freshness defaults to 30 seconds. Configure it withdefaultPreloadStaleTimeor a route'spreloadStaleTime.Preload freshness defaults to 30 seconds Set it with defaultPreloadStaleTime or a route's preloadStaleTimePreload freshness defaults to 30 secondsPreload freshness - The unused retention window defaults to 5 minutes. Configure it with
defaultPreloadGcTimeor a route'spreloadGcTime. Older unused entries are eligible for pruning during a later cache reconciliation.The unused retention window defaults to 5 minutes. Configure it withdefaultPreloadGcTimeor a route'spreloadGcTime. Older unused entries are eligible for pruning during a later cache reconciliation.Unused preload retention defaults to 5 minutes Older unused entries become eligible for pruning during a later cache reconciliationUnused preload retention defaults to 5 minutespreload retention - The speculative lane is never promoted into router state. Navigation creates its own presentation and runs its own
beforeLoadchain. It can reuse cached loader data or join a loader that is still in flight.The speculative lane is never promoted into router state. Navigation creates its own presentation and runs its ownbeforeLoadchain. It can reuse cached loader data or join a loader that is still in flight.The speculative lane is never promoted into router state Navigation runs its own beforeLoad and presentation but may reuse cached loader dataThe speculative lane is never promoted into router statespeculative lane
If you need more control over preloading, caching and/or garbage collection of preloaded data, you should use an external caching library like TanStack Query.If you need more control over preloading, caching and/or garbage collection of preloaded data, you should use an external caching library like TanStack Query.Use an external caching library like TanStack Query for more cache control It gives finer control over preloading, caching and garbage collectionUse an external caching library like TanStack Query for more cache controlTanStack Query
The simplest way to preload routes for your application is to set the defaultPreload option to intent for your entire router:The simplest way to preload routes for your application is to set the defaultPreload option to intent for your entire router:Set defaultPreload to intent on the router to enable preloading everywhere This is the simplest way to preload routes across the applicationSet defaultPreload to intent on the router to enable preloading everywheredefaultPreload
This will turn on intent preloading by default for all <Link> components in your application. You can also set the preload prop on individual <Link> components to override the default behavior.This will turn on intent preloading by default for all <Link> components in your application. You can also set the preload prop on individual <Link> components to override the default behavior.defaultPreload enables intent preloading for all Link components The preload prop on individual Link components can override the defaultdefaultPreload enables intent preloading for all Link componentsintent preloading
Preload Delay
Intent and viewport preloading start after a 50ms default delay
By default, intent focus/hover and viewport preloading start after 50ms. Pending preloads are cancelled if focus or hover ends, or the link leaves the viewport. Touch intent preloads immediately. You can change this delay by setting the defaultPreloadDelay option on your router:By default, intent focus/hover and viewport preloading start after 50ms. Pending preloads are cancelled if focus or hover ends, or the link leaves the viewport. Touch intent preloads immediately.Intent and viewport preloading start after a 50ms default delay Pending preloads cancel when hover or focus ends, but touch preloads immediatelyIntent and viewport preloading start after a 50ms default delaypreload delay
You can also set the preloadDelay prop on individual <Link> components to override the default behavior on a per-link basis.You can also set the preloadDelay prop on individual <Link> components to override the default behavior on a per-link basis.The preloadDelay prop overrides the default delay per link It lets each Link component set its own preloading delayThe preloadDelay prop overrides the default delay per linkpreloadDelay
Built-in Preloading, Freshness, and Retention
Freshness via preloadStaleTime and retention via preloadGcTime are separate
If you're using the built-in loaders, you can control how long preloaded data is considered fresh by setting either routerOptions.defaultPreloadStaleTime or routeOptions.preloadStaleTime to a number of milliseconds. By default, preloaded data is considered fresh for 30 seconds.If you're using the built-in loaders, you can control how long preloaded data is considered fresh by setting either routerOptions.defaultPreloadStaleTime or routeOptions.preloadStaleTime to a number of milliseconds. By default, preloaded data is considered fresh for 30 seconds.Preloaded data is fresh for 30 seconds by default Set defaultPreloadStaleTime or preloadStaleTime in milliseconds to change itPreloaded data is fresh for 30 seconds by defaultpreloaded data
Freshness and retention are separate. preloadStaleTime controls whether the
retained loader result can be reused without another loader call.
preloadGcTime (or defaultPreloadGcTime) controls when an unused preload
result becomes eligible for pruning during a later cache reconciliation; it
does not schedule a timer to evict the result at that exact moment. Both preload
GC options default to 5 minutes.Freshness and retention are separate. preloadStaleTime controls whether the retained loader result can be reused without another loader call. preloadGcTime (or defaultPreloadGcTime) controls when an unused preload result becomes eligible for pruning during a later cache reconciliation; it does not schedule a timer to evict the result at that exact moment.Freshness and retention are separate; both preload GC options default to 5 minutes preloadGcTime only makes a result eligible for pruning, it schedules no timerFreshness and retention are separate; both preload GC options default to 5 minutesretention
To change this, you can set the defaultPreloadStaleTime option on your router:To change this, you can set the defaultPreloadStaleTime option on your router:Set defaultPreloadStaleTime on the router to change freshness The example sets it to 10_000 millisecondsSet defaultPreloadStaleTime on the router to change freshnessdefaultPreloadStaleTime
Or, you can use the routeOptions.preloadStaleTime option on individual routes:Or, you can use the routeOptions.preloadStaleTime option on individual routes:routeOptions.preloadStaleTime overrides freshness on individual routes The example reloads preloaded data once it is more than 10 seconds oldrouteOptions.preloadStaleTime overrides freshness on individual routespreloadStaleTime
// src/routes/posts.$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => fetchPost(params.postId),
// Reload preloaded data when it is more than 10 seconds old
preloadStaleTime: 10_000,
})Client-side preloading runs each route's beforeLoad with preload: true.
Every later preload or navigation runs its own beforeLoad chain, so a
navigation observes preload: false even when an identical preload is still
active. A later lane can reuse successful settled loader data or join loader
work that is still in flight, but it never reuses beforeLoad context or an
already-settled redirect, error, or not-found result. If joined loader work
later produces a terminal outcome, all current consumers of that flight observe
it. The shouldReload option remains loader-only.Client-side preloading runs each route's beforeLoad with preload: true. Every later preload or navigation runs its own beforeLoad chain, so a navigation observes preload: false even when an identical preload is still active. A later lane can reuse successful settled loader data or join loader work that is still in flight, but it never reuses beforeLoad context or an already-settled redirect, error, or not-found result.Each preload or navigation runs its own beforeLoad chain Only loader data is shared; beforeLoad context and settled errors or redirects never areEach preload or navigation runs its own beforeLoad chainbeforeLoad chain
If a route has preload: false, its speculative lane still runs beforeLoad,
but skips that route's loader. Navigation runs beforeLoad again and performs
the skipped loader work.If a route has preload: false, its speculative lane still runs beforeLoad, but skips that route's loader. Navigation runs beforeLoad again and performs the skipped loader work.With preload false, the speculative lane skips that route's loader Navigation runs beforeLoad again and performs the skipped loader workWith preload false, the speculative lane skips that route's loaderpreload false
Preloading with External Libraries
Set preloadStaleTime to 0 to let external caches decide freshness
When integrating external caching libraries like React Query, which have their own mechanisms for determining stale data, you may want to override the default preloading and stale-while-revalidate logic of TanStack Router. These libraries often use options like staleTime to control the freshness of data.When integrating external caching libraries like React Query, which have their own mechanisms for determining stale data, you may want to override the default preloading and stale-while-revalidate logic of TanStack Router. These libraries often use options like staleTime to control the freshness of data.External caching libraries have their own stale data mechanisms They often use options like staleTime to control data freshnessExternal caching libraries have their own stale data mechanismsstale data
To let an external cache make the freshness decision, set
routerOptions.defaultPreloadStaleTime or routeOptions.preloadStaleTime to
0. Settled preload data then becomes immediately stale in the Router, while
retention still follows preloadGcTime. Overlapping preload or navigation
consumers can still share in-flight loader work, and shouldReload can still
suppress a loader call.To let an external cache make the freshness decision, set routerOptions.defaultPreloadStaleTime or routeOptions.preloadStaleTime to 0. Settled preload data then becomes immediately stale in the Router, while retention still follows preloadGcTime. Overlapping preload or navigation consumers can still share in-flight loader work, and shouldReload can still suppress a loader call.Set preloadStaleTime to 0 to let an external cache decide freshness Settled preload data becomes immediately stale while retention still follows preloadGcTimeSet preloadStaleTime to 0 to let an external cache decide freshnesspreloadStaleTime
For example:
This would then allow you, for instance, to use an option like React Query's staleTime to control the freshness of your preloads.This would then allow you, for instance, to use an option like React Query's staleTime to control the freshness of your preloads.This lets React Query's staleTime control preload freshness The external library's own freshness setting takes over the decisionThis lets React Query's staleTime control preload freshnessstaleTime
Preloading Manually
Use preloadRoute or loadRouteChunk to preload manually
If you need to manually preload a route, use the router's preloadRoute method.
It accepts a standard TanStack NavigateOptions object and returns the
speculative match lane. An ordinary error or not-found thrown while loading is
represented in that returned lane; cancellation or control flow that produces
no reusable lane can return undefined.If you need to manually preload a route, use the router's preloadRoute method. It accepts a standard TanStack NavigateOptions object and returns the speculative match lane. An ordinary error or not-found thrown while loading is represented in that returned lane; cancellation or control flow that produces no reusable lane can return undefined.router.preloadRoute manually preloads a route and returns the speculative match lane It accepts NavigateOptions; errors appear in the lane, cancellation may return undefinedrouter.preloadRoute manually preloads a route and returns the speculative match lanepreloadRoute
If you need to preload only the JS chunk of a route, you can use the router's loadRouteChunk method. It accepts a route object and returns a promise that resolves when the route chunk is loaded.If you need to preload only the JS chunk of a route, you can use the router's loadRouteChunk method. It accepts a route object and returns a promise that resolves when the route chunk is loaded.router.loadRouteChunk preloads only a route's JS chunk It accepts a route object and resolves once that chunk is loadedrouter.loadRouteChunk preloads only a route's JS chunkloadRouteChunk