Prefetching
Purpose and Scope
Prefetching means starting a query before the UI component that consumes it needs to render the result. In TanStack Query terms, the work is not a separate data layer: it is an early interaction with the same server-state cache that normal queries observe. The official product framing describes Query as giving asynchronous data a cache, lifecycle, declarative fetching APIs, refetching, invalidation, and observers across TypeScript applications. Prefetching fits that model because the warmed data is stored under the same query key that later components use, so navigation, hover handlers, route loaders, and other anticipatory flows can reduce visible loading states instead of duplicating request logic.
Sources: docs/config.json, docs/framework/angular/reference/functions/provideTanStackQuery.md
The supplied repository evidence for this page is centered on the Angular adapter, so the concrete setup examples use @tanstack/angular-query-experimental. The same mental model applies across framework adapters: a QueryClient owns the cache, components or framework-native primitives observe queries, and query functions return promises. In Angular, the application-level entry point is provideTanStackQuery(queryClient, ...features): Provider[], which installs the providers needed to enable TanStack Query functionality. A prefetching strategy should start from that shared client, because preloading data into a different client would not help the screen that later subscribes to the query.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
Relevant Source Files
docs/framework/angular/reference/functions/provideTanStackQuery.md- Defines the Angular provider setup, theQueryClientrelationship, optional features, and the advancedInjectionTokenpattern for sharing or lazy-loading a client.docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md- Explains that TanStack Query fetching is promise-based and backend/client agnostic, including how AngularHttpClientobservables are converted for query functions.docs/framework/angular/guides/background-fetching-indicators.md- ShowsinjectQuery, query status checks,isFetching, andinjectIsFetching, which are important when prefetched data is later refreshed in the background.docs/framework/angular/devtools.md- Documents enabling Angular Query Devtools withwithDevtools, including development-only loading and production lazy-loading options for inspecting warmed cache entries.docs/config.json- Shows the docs navigation structure across frameworks, which helps place prefetching among the wider getting-started, guide, and adapter documentation.docs/community-resources.md- Lists community learning resources and utilities, including query-key tooling that can help teams standardize cache identity used by reads, invalidation, and prefetching.
Core Primitives
A useful prefetching design starts by naming the shared primitives. The QueryClient is the cache owner and the object configured through Angular's provider system. A query key is the cache identity for a resource plus its inputs. A query function is the promise-returning unit of work that fetches the resource. An observer is the framework-specific subscription created by primitives such as Angular's injectQuery. When a component later calls injectQuery with the same key and compatible options, it observes the existing cache record and may render cached data while TanStack Query decides whether a background refetch is needed.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md, docs/framework/angular/guides/background-fetching-indicators.md
The Angular data-fetching guide is especially important for prefetching because it makes the fetch boundary explicit: TanStack Query is built around promises and is agnostic to the data client. Angular applications may use native fetch, graphql-request, specialized clients, or Angular HttpClient. If the project uses HttpClient, its observables must be converted with lastValueFrom or firstValueFrom inside the query function. That conversion should be shared between normal query usage and prefetch usage, so a prefetched route and the component it opens do not accidentally implement different request behavior.
Sources: docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
@Component({
// ...
})
class ExampleComponent {
private readonly http = inject(HttpClient)
readonly query = injectQuery(() => ({
queryKey: ['repoData'],
queryFn: () =>
lastValueFrom(
this.http.get('https://api.github.com/repos/tanstack/query'),
),
}))
}Execution Flow
The normal execution flow begins when the application installs TanStack Query providers. In a standalone Angular application, the docs show bootstrapApplication(AppComponent, { providers: [provideTanStackQuery(new QueryClient())] }). In an NgModule application, the same provider call appears in the module providers array. Once that provider is in place, any route, component, or service that can reach the configured client can participate in cache warming. The important constraint is consistency: the prefetch step and the consuming component must agree on the query key and on the query function's semantics.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
A typical prefetch workflow has four phases. First, choose an event that predicts demand, such as entering a route, hovering a link, opening a menu, or completing a parent query. Second, call the appropriate QueryClient prefetching API with the same key and fetching logic that the destination will use. Third, render the destination component with a normal query primitive instead of custom loading state. Fourth, let Query's freshness and background-fetching behavior decide whether the cached result can be shown immediately and whether a refresh should happen behind the scenes. The component remains declarative because it still describes the data it needs rather than manually copying prefetched values into local state.
Sources: docs/framework/angular/guides/background-fetching-indicators.md
// Conceptual Angular setup: install one shared QueryClient for the app.
import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query-experimental'
export const appConfig = {
providers: [provideTanStackQuery(new QueryClient())],
}When prefetched data is later observed, users may still see background activity. The Angular background-fetching guide shows a component that renders pending, error, and success states, and then displays Refreshing... while todosQuery.isFetching() is true. That distinction matters for prefetching: a warmed cache can prevent the first screen from being empty, but it does not mean the query will never refetch. A good UI treats prefetched data as cached server state, not as immutable local state, and surfaces refresh indicators only where they help the user understand that data is being updated.
Sources: docs/framework/angular/guides/background-fetching-indicators.md
Implementation Details
For Angular applications that use HttpClient, prefetching should preserve Angular integration rather than bypass it accidentally. The data-fetching guide calls out several benefits of HttpClient: test-time response mocking through Angular testing utilities, interceptors that integrate with dependency injection, awareness of pending tasks, and server-side rendering request caching. Because TanStack Query accepts any promise-returning query function, teams can keep those Angular capabilities by converting HttpClient observables to promises. This is often preferable to writing a second fetch-based prefetcher that skips interceptors, authentication headers, logging, or SSR request behavior already configured in Angular.
Sources: docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
Lazy-loaded Angular routes introduce an additional design choice. The provideTanStackQuery reference documents that the queryClient parameter can be either a QueryClient instance or an InjectionToken<QueryClient>. It also explains that using an injection token is an advanced optimization that can keep TanStack Query out of the main application bundle while still sharing a QueryClient for lazy-loaded routes. If prefetching happens before a lazy route is loaded, teams should be careful that the warmed data is written to the same client that the lazy route will use. Otherwise the prefetch succeeds technically but provides no cache benefit to the destination.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
Devtools are a practical part of implementing and validating prefetching. The Angular devtools guide says devtools help debug and inspect queries and mutations, and are enabled by adding withDevtools to provideTanStackQuery. By default, Angular Query Devtools are included only in development-mode bundles. The production subpath and loadDevtools option support explicit production or staging behavior, including auto, true, and false. During prefetch work, devtools let developers verify that a hover or route event created the expected cache entry, that the key matches the consuming component, and that the query is not repeatedly refetching due to accidental key churn.
Sources: docs/framework/angular/devtools.md
import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query-experimental'
import { withDevtools } from '@tanstack/angular-query-experimental/devtools'
export const appConfig = {
providers: [provideTanStackQuery(new QueryClient(), withDevtools())],
}Compact Reference
| Concern | Source-backed contract | Prefetching implication |
|---|---|---|
| Angular provider | provideTanStackQuery(queryClient, ...features): Provider[] | Install one shared query client before warming data for screens that will observe it. |
| Query client input | QueryClient or InjectionToken<QueryClient> | Use an injection token carefully when lazy routes and prefetchers must share a client. |
| Fetching model | Query functions are promise-based and client agnostic | Reuse the same promise-returning fetch function for prefetch and component queries. |
Angular HttpClient | Observables must be converted with lastValueFrom or firstValueFrom | Keep interceptors, testing support, pending-task tracking, and SSR behavior in the prefetch path. |
| Component observation | injectQuery(() => ({ queryKey, queryFn })) | The consuming component should use the same key identity as the prefetch call. |
| Background status | isFetching() and injectIsFetching() expose active fetching | Warmed data can still refresh; show subtle global or local indicators when useful. |
| Devtools feature | withDevtools() and loadDevtools options | Inspect warmed cache entries and verify production/staging loading behavior. |
Testing Signals and Next Steps
The repository-level docs configuration shows TanStack Query documentation is organized by framework and guide area, while the community resources page points readers to maintainer-authored material and utilities such as Query Key Factory. That matters because prefetching quality depends heavily on key discipline. If teams invent keys ad hoc in hover handlers, route loaders, and components, the cache becomes fragmented. A good next step is to standardize key factories for resource families, colocate reusable query option builders with data clients, and use devtools to confirm that prefetch, read, invalidate, and background refetch operations all speak the same cache language.
Sources: docs/config.json, docs/community-resources.md, docs/framework/angular/devtools.md
For implementation work, start with the smallest route or interaction where users currently see a loading gap. Ensure the app has a single shared QueryClient, extract the destination's queryKey and promise-returning queryFn into reusable code, trigger prefetch before navigation, and keep the destination component declarative with injectQuery. If the app uses Angular HttpClient, preserve its observable-to-promise conversion in that shared fetch path. Finally, turn on devtools in development and watch both the cache entry and isFetching indicators so the user experience reflects cached data plus any necessary background refresh.