Link to the code that reproduces this issue
https://github.com/mmmprod/turbopack-suspense-remount-repro
To Reproduce
- Clone the repo:
git clone https://github.com/mmmprod/turbopack-suspense-remount-repro
npm install
npm run dev (Turbopack, default in Next.js 16)
- Open http://localhost:3000 in browser
- Open browser console (F12)
- Observe
[HeavyComponent] render mount=XXXX logs — each pair has a different mount ID, meaning the component is unmounted and remounted infinitely
Current vs. Expected Behavior
Current: The lazy-loaded component enters an infinite unmount/remount cycle. Each mount gets a new React instance (different useRef value). The page never becomes interactive.
Expected: The component should mount once (+ StrictMode double), render, and stay mounted.
Only affects Turbopack dev mode
| Mode |
Result |
next dev (Turbopack) |
Infinite remount loop |
next dev --webpack |
Works perfectly |
next build && next start |
Works perfectly |
Environment
- Next.js: 16.2.2
- React: 19.2.4
- Node.js: 20.20.0
- OS: Linux (WSL2)
Component characteristics
The reproduction component has 15+ Zustand useSyncExternalStore selectors across 3 stores, useContext consumer, 4 useMemo, 3 useCallback, 2 useEffect. Loaded via React.lazy() inside Suspense.
Simpler components with 2-3 selectors do NOT reproduce.
Workaround
Eager-import the component in dev, lazy in prod:
import { HeavyComponent as Eager } from "./HeavyComponent";
const HeavyComponent = process.env.NODE_ENV === "production"
? lazy(() => import("./HeavyComponent").then(m => ({ default: m.HeavyComponent })))
: Eager;
Which area(s) are affected?
Turbopack
Which stage(s) are affected?
next dev (Development)
Link to the code that reproduces this issue
https://github.com/mmmprod/turbopack-suspense-remount-repro
To Reproduce
git clone https://github.com/mmmprod/turbopack-suspense-remount-repronpm installnpm run dev(Turbopack, default in Next.js 16)[HeavyComponent] render mount=XXXXlogs — each pair has a different mount ID, meaning the component is unmounted and remounted infinitelyCurrent vs. Expected Behavior
Current: The lazy-loaded component enters an infinite unmount/remount cycle. Each mount gets a new React instance (different
useRefvalue). The page never becomes interactive.Expected: The component should mount once (+ StrictMode double), render, and stay mounted.
Only affects Turbopack dev mode
next dev(Turbopack)next dev --webpacknext build && next startEnvironment
Component characteristics
The reproduction component has 15+ Zustand useSyncExternalStore selectors across 3 stores, useContext consumer, 4 useMemo, 3 useCallback, 2 useEffect. Loaded via React.lazy() inside Suspense.
Simpler components with 2-3 selectors do NOT reproduce.
Workaround
Eager-import the component in dev, lazy in prod:
Which area(s) are affected?
Turbopack
Which stage(s) are affected?
next dev (Development)