HydrationBoundary ignores/skips server-prefetched query when a parent useQuery with same key is rendered before child useSuspenseQuery
Les mainteneurs répondent en général sous 1 jour
@mgarcialeniolabs y travaille déjà.
Depuis le 27/8/2026.
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 52/100
- Type d'issue
- Bug
- Clarté
- Plutôt claire
- Activité
- Active
- Stack technique
- react, typescript
- Domaine
- frontend
Piste de recherche
Commencez par la reproduction CodeSandbox liée et suivez le comportement de HydrationBoundary autour de useQuery du parent et de useSuspenseQuery de l’enfant, en vous appuyant sur le flux de app/(dashboard)/todos/[todoId]/layout.tsx ou page.tsx décrit dans le rapport. Comparez les états du prefetch côté serveur, de la dehydration et de la hydration ; le travail est terminé lorsque la clé préchargée reste disponible pour l’enfant sans requête SSR en double ni erreur du client-request guard.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Describe the bug
If a useQuery is rendered before a HydrationBouhndary, hydrations for that key will be skipped despite the fact that the useQuery would have not fetched in the first place.
In our case we have a prefetch where we pass the dehydrated queryClient to a HydrationBoundary. But there is a useQuery using the same key as the prefetched query above the hydration boundary that (in some global component higher up) gets rendered. The hydration boundary skips this hydration and the child components of the HydrationBoundary that have useSuspenseQuery for that key still run their queries in SSR.
Small example:
function Page({ params }) {
const queryClient = getQueryClient();
void queryClient.prefetchQuery(
buildTodoQueryOptions(params.todoId, makeServerRequest),
);
return (
<>
<SomeHeaderComponent /> {/* a `useQuery` lives in here */}
<HydrationBoundary state={dehydrate(queryClient)}>
<MyChildComponent /> {/* a `useSuspenseQuery` lives in here and fires despite the above prefetch */}
</HydrationBoundary>
</>
);
}
Also, in our particular case this triggers a guard we have to ensure we do not client fetch on the server during SSR. This also means the request is being duplicated and firing twice (in our prefetch and during SSR, which should have been skipped)
Flow (all on the server):
- Parent/route boundary prefetches the detail query
todoIdon the server. - A higher component in layout/header renders a
useQuerywith the keytodoId. - Pass the dehydrated and prefetched
queryClientto aHydrationBoundarylower than theuseQuery. - The HydrationBoundary` skips this key and does not hydrate the prefetched query as expected.
- Child
useSuspenseQuerythen attempts a fetch with client requestor during SSR and hits the guard error.
Repro
Server prefetch at detail layout/page:
// In app/(dashboard)/todos/[todoId]/layout.tsx or page.tsx
function Page({ params }) {
const queryClient = getQueryClient();
void queryClient.prefetchQuery(buildTodoQueryOptions(todoId, makeServerRequest));
return <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>;
}
Global/header client component that also reads URL todoId (lives OUTSIDE/above the above layout.tsx)
"use client";
import { useQuery, useParams } from "@tanstack/react-query";
function TodoHeaderMenu() {
const params = useParams();
const todoId = typeof params.todoId === "string" ? params.todoId : "";
const { data: todo } = useQuery({
...buildTodoQueryOptions(todoId),
enabled: Boolean(todoId),
});
return <span>{todo?.title}</span>;
}
Child detail component:
"use client";
function TodoPage({ todoId }: { todoId: string }) {
const { data: todo } = useSuspenseQuery(buildTodoQueryOptions(todoId));
return <TodoDetails todo={todo} />;
}
buildTodoQueryOptions defaults to client requestor. Client-request guard throws:
if (typeof window === "undefined") {
throw new Error(
"Warning, aborting SSR. You attempted to fetch from the server with clientRequest."
);
}
AI disclosure: I had codex help me with the quick repro.
Questions
- Is this current behavior expected?
- Should hydration logic be changed to only skip queries that are fetching (aka don't skip
useQueriesthat fill the cache key on the server but do not initiate filling the value on the server)
Your minimal, reproducible example
https://codesandbox.io/p/devbox/pr7c4g
Steps to reproduce
The home page on that will bring up the error in the next dev error popup
Expected behavior
I would expect a useQuery in a higher component with the same key to not STOP the server fetch/hydration given useQuery would not trigger the client request guard like useSuspenseQuery does. useQuery does not start the queryFn on the server, so it should not stop useSuspenseQueries that CAN imo.
This also comes with the additional problem that because of the useQuery, the useSuspenseQuery is firing again, ruining deduplication.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
macOS
Chromium
next: 16.1.1
Tanstack Query adapter
react-query
TanStack Query version
5.90.21
TypeScript version
No response
Additional context
I can technically gate the useQuery in the parent component with something like
const isDealQueryReady = !!(
state &&
state.data !== undefined &&
state.status !== "pending" &&
state.fetchStatus !== "fetching"
)
//and then
if (!isDealQueryReady) {
return null;
}
//...
//...`useQuery`
and then I do not get the error but that is obnoxious.
- Langage dominant
- TypeScript
- Étoiles
- 50.4k
- Forks
- 4.3k
- Merge moyen
- 12 h 18 min
- PR mergées (30 j)
- 488
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de TanStack/query
-
QueryClient.isFetching and useIsFetching accept a fetchStatus filter and ignore itPeut-être pris @Apu-Emdad l’a pris il y a 4 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
solid-query: STRICT_READ_UNTRACKED on Solid 2 — client()/options() read in component body (useMutation, useBaseQuery)Peut-être à nouveau libre @LeonxLJX l’a pris il y a 44 jours, et aucune pull request n’est ouverte. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
TanStack/query#11358 · 2 commentaires · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
solid-query 6.0.0-rc.5: onCacheEvent throws NotReadyError during a Solid 2 (rc.14+) transition landingPeut-être pris @UzumakiODZ l’a pris il y a 3 jours. Ouverte
Difficulté 4/5 3-5 jours Accessibilité débutants 28/100
TanStack/query#12031 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
Explicit gcTime schedules a GC timer during SSR, retaining the whole render in memoryPeut-être pris @AdzerKI l’a pris il y a 4 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 25/100
TanStack/query#12025 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
restoreQueries and persisterGc throw on malformed persisted entriesPeut-être pris @VGontier-cmd l’a pris il y a 12 jours. Ouverte
Difficulté 3/5 1-2 jours Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de TanStack/query
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
Les mainteneurs répondent en général sous 1 jour
-
Pet submission: Sleepwing / 眠翼鸟Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
OpenPetsHQ/openpets#273 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
fritzprix/libr-agent#2161 ·
Les mainteneurs répondent en général sous 1 jour
-
macOS: clicking a menu item that has a submenu closes the menu instead of opening the submenuOuverte
Difficulté 2/5 1-3 heures Accessibilité débutants 83/100
blackboardsh/electrobun#581 ·
-
backlog-skill effort:xs frontend infra ready-for-agent
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
LucasSantana-Dev/Lucky#2862 ·
Les mainteneurs répondent en général sous 1 jour