Paginated Queries

Paginated queries cover the common interface where one screen shows one page, cursor, or slice of a remote collection at a time. In TanStack Query, pagination is built from normal queries rather than a separate pagination cache. The page value becomes part of the query key, so every page has an independent cache identity while still using the same declarative fetching model. The important user experience problem is preventing the screen from flashing back to an initial loading state every time the page changes. TanStack Query solves that with placeholder data that can keep the last successful page visible while the next page request is in flight.

Sources: docs/framework/react/guides/paginated-queries.md, examples/react/pagination/src/pages/index.tsx

Purpose and Scope

A paginated query is useful when the server response includes a bounded set of rows plus navigation metadata such as whether another page exists. The React guide starts with the simplest form: include the page value in the query key and fetch the corresponding page. That already gives correct cache behavior, because changing the key creates or selects a different query. The drawback is visual churn: without a lagging strategy, each new page can look like a brand new pending query. This page focuses on the documented lagged-query pattern, not infinite scrolling, although the same placeholder concept also applies to infinite query keys.

The repository examples show the recommended shape for a page-based list. The data fetcher accepts a page number and returns a promise for an object containing projects and a has-more flag. The component stores the current page locally, passes that page into the query key, and renders navigation buttons. The key design is what makes going backward feel instant: earlier pages remain cached as ordinary queries. The placeholder design is what makes going forward feel stable: the last successful page stays on screen until the requested page finishes loading.

Sources: docs/framework/react/guides/paginated-queries.md, examples/react/pagination/src/pages/index.tsx

Relevant Source Files

  • docs/framework/react/guides/paginated-queries.md - Defines the React paginated and lagged query guide, including the page-based key, the loading-state problem, placeholder data, the exported helper, the placeholder flag, and the button disabling pattern.
  • docs/framework/angular/guides/paginated-queries.md - Provides the Angular version of the same guide, mapping hooks to injection functions and showing signals, injected query client access, prefetching, and template status handling.
  • examples/react/pagination/src/pages/index.tsx - Implements the React pagination example with a query client provider, page state, a typed fetcher, placeholder data, stale time, next-page prefetching, devtools, and navigation controls.
  • examples/angular/pagination/src/main.ts - Shows the Angular pagination example entry point, where Angular bootstraps the application component with the example application configuration.

Core Primitives

The core primitives are the query key, the query function, placeholder data, and status flags. The query key is the cache contract: a stable resource label plus the current page value. The query function reads that page and requests the corresponding server data. Placeholder data lets a query with a new key temporarily present data from the prior successful query, and the exported helper provides the common previous-data behavior directly. The placeholder flag tells the UI that the visible data is lagged, not final. The fetching flag remains important because the pending status may not reappear while previous data is being shown.

The React and Angular guides use the same conceptual contract with framework-native APIs. React code calls the query hook and receives fields such as status, data, error, fetching state, and placeholder state. Angular code uses signal-based injection, so the same values are read through callable accessors in a component template and class. That adapter difference matters for syntax, but not for cache identity or lifecycle. In both cases, page changes are expressed by changing the value inside the query key, and both examples use the has-more field to prevent navigation beyond known server results.

Sources: docs/framework/react/guides/paginated-queries.md, docs/framework/angular/guides/paginated-queries.md

System-to-Code Mapping

ConcernReact implementationAngular implementation
Page identity['projects', page]['projects', page()]
FetchingPage-aware fetchProjects(page)Observable converted for the current signal page
Lagged dataplaceholderData: keepPreviousDataplaceholderData: keepPreviousData
Background indicatorisFetchingquery.isFetching()
Placeholder guardisPlaceholderDataquery.isPlaceholderData()
Next page warmingqueryClient.prefetchQuery in an effectInjected QueryClient with prefetchQuery inside an effect

The mapping shows that pagination is mostly a composition pattern rather than a special adapter feature. The framework layer supplies the idiomatic state primitive, rendering syntax, and dependency access, while the underlying query behavior remains consistent. React stores the page in component state and obtains the client through a query-client hook. Angular stores the page in a signal and injects the client through Angular dependency injection. Both versions use an effect to prefetch the next page only after the current response confirms that another page exists and the currently displayed data is not placeholder data.

Execution Flow

Start by creating a query client and placing the application under the framework provider. The React example instantiates a client, renders a provider, and then mounts the pagination example. Inside the example, the current page starts at zero. The fetcher requests the server endpoint with that page and expects a response containing an array of projects plus a flag indicating whether another page can be requested. The query uses the projects key plus the page value, so changing from page zero to page one does not overwrite the first page cache entry.

When the user clicks Next, the page state changes. TanStack Query sees a new key and begins fetching the next page. Because placeholder data is configured to keep previous data, the component can remain in a successful visual state using the old page while the new network request proceeds. This is why the guides recommend showing a small background loading indicator from the fetching flag rather than returning the whole screen to a loading placeholder. When the response arrives, the old page data is replaced with the new page data and the placeholder flag turns off.

Navigation controls should respect the difference between known data and placeholder data. The previous button can be disabled when the current page is already the first page. The next button should be disabled while placeholder data is being shown or when the visible data does not indicate another page. Without that guard, a user could advance based on stale navigation metadata from the previous page. The examples also use prefetching to warm the next page after a successful response. If the user then advances, the target page may already be in the cache and can render immediately while still being eligible for background refetching.

Sources: docs/framework/react/guides/paginated-queries.md, examples/react/pagination/src/pages/index.tsx

Angular Adapter Flow

The Angular guide preserves the same pagination behavior while presenting it in Angular terms. The component stores the page as a signal, defines an injected query whose options are derived from the current signal value, and renders status branches in the template. Because query values are exposed as signals or signal-like accessors, the template checks pending, error, data, placeholder, and fetching state with function calls. The guide also injects the query client privately and uses an effect to prefetch the next page after the current page proves that a next page exists.

The supplied Angular example entry point is intentionally small: it bootstraps the application component with the example configuration and logs bootstrap errors. That file is still important because it locates the pagination example in a standard Angular application lifecycle. The more detailed pagination logic is documented in the Angular guide, including the provider-backed query client access, the page signal, and the same prefetching guard used by React. Readers porting the React example to Angular should translate state and rendering syntax, not redesign the query key or placeholder strategy.

Sources: docs/framework/angular/guides/paginated-queries.md, examples/angular/pagination/src/main.ts

API Components and Options

Use a page-based key whenever the page or cursor changes which data should be fetched. Use the query function to close over the current page in React, or derive it from the current signal in Angular. Configure placeholder data with the exported previous-data helper when the desired transition is to keep the last successful page visible. Read the placeholder flag before allowing a next-page transition, because the data currently on screen may still describe the previous page. Read the fetching flag for background progress messaging, especially when the primary status remains successful during a lagged transition.

The examples include a finite stale time for the page queries. That detail is useful because prefetching and back navigation interact with freshness. A warmed next page can be served from cache when the user navigates, and a previously visited page can appear instantly when going backward. If the data is stale, TanStack Query can still refetch in the background while preserving the cached result for display. This produces the intended page navigation experience: responsive movement between cache entries, visible background activity, and fewer full-screen loading transitions after the first page has succeeded.

Next Steps

After implementing this pattern, review Query Keys to make sure the page, filters, search terms, and resource scope are all represented in the key. Review Prefetching if you want to warm pages on hover, route transition, or viewport intent instead of only after the current page loads. Review Infinite Queries when the UI appends pages into one growing list rather than replacing the visible page. For mutation-heavy lists, pair pagination with invalidation rules so edits, creates, or deletes refresh the correct project list pages without discarding the stable navigation experience.