T
TanStack9mo ago
genetic-orange

HydrationBoundary fails to work when the layout itself is wrapped around HydrationBoundary component

Hey, I'm trying to wrap my head around this bug. I've a header that lists organizations and the prefetch for those organizations happens on a layout.tsx:
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
const queryClient = new QueryClient()
const token = await getToken()

await queryClient.prefetchQuery({
queryKey: ['organizations'],
queryFn: async () => (await getOrganizations({ token })).unwrap(),
})

return (
<div className="w-full h-screen">
<HydrationBoundary state={dehydrate(queryClient)}>
{children}
</HydrationBoundary>
</div>
)
}
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
const queryClient = new QueryClient()
const token = await getToken()

await queryClient.prefetchQuery({
queryKey: ['organizations'],
queryFn: async () => (await getOrganizations({ token })).unwrap(),
})

return (
<div className="w-full h-screen">
<HydrationBoundary state={dehydrate(queryClient)}>
{children}
</HydrationBoundary>
</div>
)
}
So far so good, the organizations are hydrated correctly and no flickers shows up on the header. But now I've a page.tsx for listing projects and I also would like to hydrate those projects:
export default async function Projects({
params: { organizationId },
}: {
params: { organizationId: string }
}) {
const queryClient = new QueryClient()

await queryClient.prefetchQuery({
queryKey: ['projects', organizationId],
queryFn: async () => {
const token = await getToken()
const projectsResult = await getProjects({
token,
organizationId,
})

const projectsData = projectsResult.unwrap()

return projectsData
},
})

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<ProjectsPage />
</HydrationBoundary>
)
}
export default async function Projects({
params: { organizationId },
}: {
params: { organizationId: string }
}) {
const queryClient = new QueryClient()

await queryClient.prefetchQuery({
queryKey: ['projects', organizationId],
queryFn: async () => {
const token = await getToken()
const projectsResult = await getProjects({
token,
organizationId,
})

const projectsData = projectsResult.unwrap()

return projectsData
},
})

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<ProjectsPage />
</HydrationBoundary>
)
}
But for some reason the <ProjectsPage> which is a client component doesn't hydrate correctly and thus the flickers shows up. I can see that the useQuery data of said page is empty at first render and also the queryClient.getQueryCache().getAll() shows the query key but at a pending state which may show that the react-query didn't use the state from the page and it's trying to fetch up again. What I'm doing wrong? I'm guessing it's related to NextJS SSR?
1 Reply

Did you find this page helpful?