Scroll Restoration
Purpose and Scope
Scroll restoration is the user experience of returning a person to the same visual position after they navigate away from a list, detail page, route segment, or tab and then come back. TanStack Query does not replace the router or browser mechanism that stores scroll coordinates. Its role is to keep server state available across navigation boundaries so the returning screen can render stable content immediately instead of collapsing into a loading-only view, changing item heights, or refetching in a way that shifts the page before the router can restore position.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md, docs/framework/angular/guides/background-fetching-indicators.md
The repository evidence for this page comes from the Angular documentation, but the idea applies across TanStack Query adapters: components declare the remote data they need, and the shared QueryClient cache coordinates reuse, refetching, and observation. In an Angular app, that starts by providing a QueryClient with provideTanStackQuery. Once the provider is installed, route components can use query APIs against the same client, which is the foundation for keeping previously fetched data available when navigation returns to a screen.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
Relevant Source Files
docs/framework/angular/reference/functions/provideTanStackQuery.md- Documents the Angular provider entry point, theQueryClientdependency, optional features, and provider return type used to enable cache-backed query behavior in an Angular application.docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md- Explains that TanStack Query fetches through Promises, can use AngularHttpClient, and highlights SSR request caching versus TanStack Query hydration decisions that affect navigation stability.docs/framework/angular/guides/background-fetching-indicators.md- Shows query-level and global background fetching indicators usinginjectQuery,isFetching, andinjectIsFetching, which are useful when a restored screen is showing cached data while refreshing.docs/framework/angular/devtools.md- Shows how to enable Angular Query Devtools throughwithDevtools, providing a way to inspect cached queries and mutations while validating navigation and restoration behavior.docs/config.json- Places Angular framework pages, quick start material, devtools, and guide content in the official docs navigation structure, which is useful for following the setup path around this workflow.docs/community-resources.md- Lists community learning material and utilities, including query-key and code-generation resources that can help teams standardize cache identity for navigation-heavy applications.
System-to-Code Mapping
For scroll restoration to feel correct, the page that is being restored must be able to reconstruct its content from the same cache identity it used before navigation. In TanStack Query terms, that identity is the query key, and the owning runtime object is the QueryClient. The Angular provider documentation defines provideTanStackQuery(queryClient, ...features): Provider[], describes passing either a QueryClient instance or an Angular InjectionToken, and shows both standalone and NgModule setup. That provider layer is the code-to-application bridge for all later query reads.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
The advanced InjectionToken example is relevant to navigation because it allows a QueryClient to be supplied from a lazy loaded route or component provider while still sharing the client intentionally. That is a bundle optimization, not the default recommendation, and the docs note that most applications should provide the QueryClient in the main application config. For scroll-sensitive route transitions, the practical takeaway is to be deliberate about QueryClient lifetime: recreating clients per screen can discard cached data that would otherwise help a returning view paint consistently.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
Data fetching implementation also matters. The Angular data-fetching guide says TanStack Query is built on Promises and can use any asynchronous client, including browser fetch, graphql-request, and Angular HttpClient. When using HttpClient, observables are converted with lastValueFrom or firstValueFrom inside the queryFn. For scroll restoration, the important constraint is not which transport is used, but that the query function resolves into cacheable data under a stable key so the restored route can reuse prior results while a background refresh proceeds.
Sources: docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
Navigation and Restoration Flow
A typical route flow starts when a list screen mounts and calls injectQuery with a key such as ['todos']. The query function fetches the list, the cache stores the result, and the template renders items. When the user opens a detail route, the list component may unmount, but the QueryClient can still retain the cached query according to its normal lifecycle. If the user returns before the data is garbage collected, the list can render cached data quickly, which gives the router or browser a stable document to restore against.
Sources: docs/framework/angular/guides/background-fetching-indicators.md, docs/framework/angular/reference/functions/provideTanStackQuery.md
The background fetching guide demonstrates the user interface distinction that matters after a restore: pending state is different from background fetching state. The sample Angular component shows Loading... only while the query is pending, then renders successful data and conditionally shows Refreshing... when todosQuery.isFetching() is true. That pattern avoids replacing already visible content with a loading placeholder during a refetch, which is one of the most practical ways TanStack Query supports smooth back-and-forward navigation.
Sources: docs/framework/angular/guides/background-fetching-indicators.md
@Component({
selector: 'todos',
template: `
@if (todosQuery.isPending()) {
Loading...
} @else if (todosQuery.isError()) {
An error has occurred: {{ todosQuery.error().message }}
} @else if (todosQuery.isSuccess()) {
@if (todosQuery.isFetching()) {
Refreshing...
}
@for (todos of todosQuery.data(); track todo.id) {
<todo [todo]="todo" />
}
}
`,
})
class TodosComponent {
todosQuery = injectQuery(() => ({
queryKey: ['todos'],
queryFn: fetchTodos,
}))
}For application-wide feedback, the same guide exposes injectIsFetching. That function supports a global loading indicator that appears when any query is fetching in the background. In a navigation scenario, a global indicator is often better than replacing route content because it communicates activity without disrupting scroll anchoring. A restored page can keep showing cached rows, cards, or details while the indicator tells the user that fresh data is being checked. This is especially useful for dashboards and list/detail flows where route changes are frequent.
Sources: docs/framework/angular/guides/background-fetching-indicators.md
Angular Setup Reference
The minimal Angular setup for cache-backed navigation is to create a QueryClient and register it in the application providers. The provider documentation shows bootstrapApplication(AppComponent, { providers: [provideTanStackQuery(new QueryClient())] }) for standalone applications and an equivalent NgModule providers array for module-based apps. Optional features are passed after the client. This shape matters because all route components need to resolve the same configured query runtime if cached data should outlive a single component instance.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
import {
provideTanStackQuery,
QueryClient,
} from '@tanstack/angular-query-experimental'
bootstrapApplication(AppComponent, {
providers: [provideTanStackQuery(new QueryClient())],
})When debugging restoration behavior, enable devtools through the same provider path. The Angular devtools guide says withDevtools can be added to provideTanStackQuery, and that the devtools are included in development mode by default. The production subpath can be used when teams want to lazy load devtools outside development, and loadDevtools can be auto, true, or false. For this workflow, devtools help confirm whether the route is using cached data, refetching, or mounting a new cache unexpectedly.
Sources: docs/framework/angular/devtools.md, docs/framework/angular/reference/functions/provideTanStackQuery.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())],
}SSR, Fetching Clients, and Layout Stability
Server rendering adds another layer to scroll and navigation stability because the first client render should match useful server-rendered content as closely as possible. The Angular data-fetching guide notes that HttpClient automatically informs Angular pending tasks and that unit tests and SSR can use application stableness information to wait for pending requests. It also explains that HttpClient can cache requests performed on the server to prevent unnecessary client requests, while TanStack Query provides its own hydration functionality that can be more powerful but requires setup.
Sources: docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
For restoration-oriented pages, choose the data path that makes the first paint and the return paint predictable. If Angular HttpClient SSR caching is enough for the route, it can prevent duplicate client requests after server render. If the application needs TanStack Query hydration, preloaded cache state can allow the client QueryClient to start with server data. In both cases, avoid rendering a very different loading skeleton when data already exists, because large layout changes make browser and router scroll restoration appear broken even when coordinates are technically restored.
Sources: docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md, docs/framework/angular/guides/background-fetching-indicators.md
Practical Checklist
Use one application-level QueryClient unless you have a deliberate lazy-loading optimization. Keep query keys stable for the route data that controls page height, such as list filters, pagination inputs, or selected scopes. Render cached successful data during background refetches, and expose refresh state with local isFetching or a global injectIsFetching indicator rather than removing content. If SSR is involved, decide whether Angular HttpClient SSR caching or TanStack Query hydration is the right mechanism for avoiding duplicate client work.
Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md, docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md, docs/framework/angular/guides/background-fetching-indicators.md
Use devtools while testing back, forward, and route-to-route transitions. Inspect whether the expected query key remains in the cache, whether a returning screen is pending or merely fetching, and whether multiple QueryClient providers accidentally divide the cache. The docs navigation in docs/config.json places Angular quick start, devtools, and guide pages under the framework section, so the recommended next step is to read the framework setup material, then validate the exact route pattern in devtools. Community resources can help teams standardize query-key factories for larger apps.
Sources: docs/framework/angular/devtools.md, docs/config.json, docs/community-resources.md