Suspense

Purpose and Scope

Suspense is the React rendering pattern where a component can pause rendering while asynchronous data is being prepared, then resume once the data is available. In TanStack Query, Suspense is a React adapter concern layered on top of the same server-state model used by every framework package: a QueryClient owns cache state, query keys identify resources, query functions return promises, and observers expose transitions such as pending, success, error, and fetching. The official product framing describes Query as a cache and lifecycle for async server state; Suspense changes how React displays an unresolved read, not what the cache stores or how fetches are coordinated.

This page helps React users decide where Suspense boundaries, error boundaries, and Query APIs fit together. In a non-Suspense component, the component often branches on loading, error, and success flags before rendering data. With Suspense-style React Query usage, the first blocking load is delegated to a Suspense fallback, while failures should be handled by an error boundary and reset flow. The data-rendering component can then focus on the successful shape of the data, but the surrounding route, page, or feature shell must intentionally own loading and recovery UI.

Sources: docs/config.json, docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md, docs/framework/angular/guides/background-fetching-indicators.md

Relevant Source Files

  • docs/config.json — Defines the documentation navigation and shows React as a first-class framework section alongside Solid, Vue, Svelte, Lit, and Angular. That supports treating Suspense as a React adapter topic rather than a query-core-only topic.
  • docs/framework/angular/reference/functions/provideTanStackQuery.md — Documents the framework-provider contract through provideTanStackQuery(queryClient, ...features): Provider[], including the required QueryClient and optional features such as devtools. React uses different component names, but the architectural need for a provided QueryClient is shared.
  • docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md — Establishes that TanStack Query fetching is promise-based and client-agnostic, including fetch, graphql-request, and Angular HttpClient after Observable-to-Promise conversion. This promise contract is what lets React Suspense wait for query work.
  • docs/framework/angular/guides/background-fetching-indicators.md — Shows the status vocabulary that adapters expose: isPending, isError, isSuccess, isFetching, and a global injectIsFetching signal. Suspense changes initial pending rendering in React, but background fetching remains a separate user-interface concern.
  • docs/framework/angular/devtools.md — Documents devtools as a way to inspect queries and mutations, including withDevtools and loadDevtools behavior. Suspense does not remove the need to inspect cache keys, observers, fetch state, retries, and errors during development.
  • docs/community-resources.md — Lists maintainer-authored articles, talks, utilities, and learning resources, including React Query material useful for deeper Suspense and boundary-design patterns.

Core Primitives

A Suspense-enabled React tree still begins with Query’s core primitives. A QueryClient owns query and mutation caches, default options, retry behavior, freshness decisions, and notification of subscribers. The Angular reference documents the same concept as provideTanStackQuery, which accepts either a QueryClient or an Angular InjectionToken<QueryClient> and returns framework providers. In React, the equivalent responsibility is handled by QueryClientProvider: it makes one stable client available to the component tree so Suspense hooks can read from a shared cache instead of creating isolated request state per component.

Query functions are the second primitive. The Angular data-fetching guide states that TanStack Query’s fetching mechanisms are built agnostically on Promises and can use many async clients. For React Suspense, this matters more than the transport library. If a query has no usable cached data, the Suspense-aware hook can rely on the query function’s returned promise to represent the pending work. If your data client is Observable-based, callback-based, or otherwise non-Promise-native, the same rule applies: wrap it so the query function resolves with data or rejects with an error through the returned promise.

Query keys are the third primitive even though the requested source snippets emphasize them only through examples. The background-fetching guide’s ['todos'] and the HttpClient guide’s ['repoData'] examples show keys as the cache identity passed with each query function. In Suspense, stable keys are especially important because they determine whether React sees a cache hit, an already in-flight promise, or a new unresolved read. When multiple components use the same key, Query can coordinate fetching and cached data; when keys change accidentally, the UI may suspend more often than expected.

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

Suspense Execution Flow

A typical React flow starts by constructing a single QueryClient, placing it above the app with QueryClientProvider, and wrapping the route or panel that needs blocking data in a React Suspense boundary. Inside that boundary, a Suspense-specific React Query hook or Suspense-enabled query read uses a query key and query function. If the cache already contains usable data, rendering continues immediately. If the cache has no usable data and the query must fetch, React displays the boundary fallback while TanStack Query drives the promise-based fetch and updates the cache when it resolves.

When the promise resolves successfully, Query stores the result, notifies observers, and React retries rendering the subtree. The component receives data in the success path and can avoid a local initial-loading branch. If the promise rejects, error UI belongs at an error boundary that can describe the failure and provide reset or retry behavior. Boundary placement is a product decision: a whole route boundary is appropriate when the page cannot make sense without its data, while a nested boundary is better when one card, tab, or sidebar can fail without hiding the rest of the screen.

Background refetching should be designed separately from first-load suspension. The Angular background-fetching example renders data when the query is successful and separately shows Refreshing... when isFetching is true. React Suspense users should preserve that distinction. A boundary fallback is usually for the first blocking read, not for every network request after data is visible. Once a page has cached data, a later refetch can keep the current UI on screen and expose a smaller indicator, disabled action, or global fetching bar rather than replacing the whole view with the initial fallback.

Sources: docs/framework/angular/guides/background-fetching-indicators.md, docs/framework/angular/devtools.md

Compact API and Configuration Reference

AreaConcrete source-backed contractSuspense relevance
Provider setupprovideTanStackQuery(queryClient, ...features): Provider[] accepts QueryClient or InjectionToken<QueryClient> and optional QueryFeaturesReact uses QueryClientProvider for the same role: a stable client must exist above Suspense query consumers
Query function contractQuery functions are promise-based; Observable clients can be adapted with lastValueFrom or firstValueFromSuspense waits on the promise represented by the query, so the returned promise must match the real async lifecycle
Query example keysqueryKey: ['repoData'] and queryKey: ['todos'] appear in supplied docs examplesStable keys decide cache identity, deduplication, and whether a Suspense read can use cached data
Status methodsisPending(), isError(), isSuccess(), isFetching() are shown in adapter-facing examplesSuspense handles the initial pending path, while success, error, and fetching semantics still guide UI design
Global fetching indicatorinjectIsFetching() reports whether queries are fetching in the backgroundReact users use the analogous global fetching concept to show app-level activity outside local Suspense fallbacks
Devtools featurewithDevtools() can be added to provider setup; loadDevtools supports 'auto', true, or falseUse devtools to inspect whether a fallback comes from a cache miss, retry, refetch, or unstable key

This reference table is intentionally cross-adapter because the supplied repository evidence is Angular-focused, but the contracts are the shared TanStack Query concepts that React Suspense builds on. The exact React hook names belong in the React hooks reference, while this page focuses on boundary responsibilities and runtime behavior. The most important implementation constraint is that Suspense does not create a second data layer. It reads through Query’s cache, so provider placement, key design, promise behavior, retry configuration, and devtools inspection remain the same categories you use for ordinary queries.

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, docs/framework/angular/devtools.md

Error Boundaries, Reset, and Debugging

Error boundaries are the companion to Suspense boundaries. Suspense can describe what users see while a blocking read is pending; an error boundary describes what users see when that read fails after Query’s configured retry behavior. A good boundary is narrow enough that the user understands what failed, but broad enough to cover all components that depend on the same critical data. For example, a route-level issue detail page may use one boundary, while a dashboard with independent widgets may use several nested boundaries so one failed widget does not hide unrelated cached data.

Debugging a Suspense page starts with the cache, not only the React tree. The Angular devtools documentation describes devtools as a way to inspect queries and mutations, and that is exactly what React teams need when a fallback appears unexpectedly. Check whether the query key is stable, whether the QueryClient is recreated on render, whether the query function is returning the correct promise, and whether the query is actually retrying or refetching in the background. If a fallback never appears, confirm whether data was prefetched, still fresh, or already restored from cache.

Testing should assert user-visible boundary behavior. Cover the initial unresolved-promise path, the successful render after resolution, the error-boundary path, and a reset or retry interaction. Also include a background-refetch case after data is already visible. The supplied background-fetching docs make the important distinction between pending and fetching explicit, and Suspense-heavy tests should keep that distinction. A passing test suite should prove that initial blocking loads show the correct fallback while later refetches preserve useful content whenever that is the intended experience.

Sources: docs/framework/angular/guides/background-fetching-indicators.md, docs/framework/angular/devtools.md

Next Steps

After this page, read the React hooks reference for the concrete React Suspense hook names, inputs, and return behavior. Then pair Suspense with the cache lifecycle, query keys, retries and cancellation, and SSR and hydration pages, because those topics determine when data is ready, when errors surface, and whether a route can be prefetched before the boundary renders. For deeper community guidance, the community resources page points to maintainer-authored articles and videos about practical React Query patterns, global-state tradeoffs, and API design decisions that influence how teams place boundaries.

Sources: docs/community-resources.md, docs/config.json