facebook / react · Issue No. 37620
On full page load, a client component calls use(browser()) (Suspense fallback is in the SSR HTML), then use() with a promise made on the client. On hydration the fallback remounts and the Progress animation starts again, even though loading is not done.
We hit this on /client-promise. Repro uses a client window.setTimeout promise after use(browser()). Real usage is the same shape when loading with Web APIs (for example localStorage, IndexedDB, or other window APIs).
/loading-only (same Progress, not a Suspense fallback) and /server-promise (promise from the server) do not restart.
Related earlier PR: https://github.com/react/react/pull/24236 (reverted in https://github.com/react/react/pull/24434).
React version: 19.3.0 (Next.js 16.3.4)
Link to code example:
https://suspense-fallback-hydration-reset.vercel.app/
https://github.com/michaltarasiuk/suspense-progress-hydration-reset
"use client";
import { use, Suspense } from "react";
import { browser } from "react-dom";
let p: Promise<number> | undefined;
function getClientPromise() {
p ??= new Promise((r) => window.setTimeout(() => r(1), 3000));
return p;
}
function ClientPromise() {
use(browser());
use(getClientPromise());
return <div>Loaded</div>;
}
export default function Page() {
return (
<Suspense fallback={<Loading />}>
<ClientPromise />
</Suspense>
);
}
The SSR fallback remounts on hydration. The Progress animation starts again.
The SSR fallback stays mounted on hydration. The animation runs without restart until loading finishes.
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.