Bug: Suspense fallback remounts on hydration after use(browser()) + client use(promise)
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)
Steps To Reproduce
- Open https://suspense-fallback-hydration-reset.vercel.app/client-promise in the address bar (full page load).
- In DevTools: Disable cache, set CPU to 4x slowdown. Hard refresh. The Progress bar jumps back near hydration.
- Hard refresh https://suspense-fallback-hydration-reset.vercel.app/server-promise and https://suspense-fallback-hydration-reset.vercel.app/loading-only. The animation does not jump on those pages.
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 current behavior
The SSR fallback remounts on hydration. The Progress animation starts again.
The expected behavior
The SSR fallback stays mounted on hydration. The animation runs without restart until loading finishes.
Source: react/react