How to Fix: Page Router – Turbopack HMR Warning: window.next.router undefined during appIsrManifest event
The warning appears because Turbopack HMR can emit an appIsrManifest update before the legacy Page Router client runtime has finished attaching window.next.router. In projects that mix dynamic imports, dev-only hot updates, and the Pages Router, that timing gap produces noisy console warnings even though navigation may still work.
Understanding the Root Cause
This issue is fundamentally a runtime ordering problem in development mode. With the Page Router, the browser-side router instance is usually exposed through window.next.router after the client bootstrap completes. During Hot Module Replacement, Turbopack pushes update events through its dev client, including metadata such as the ISR manifest. If that event is processed before the router object exists, any code path that assumes window.next.router is already initialized will hit an undefined access and log a warning.
The problem is more visible in setups using dynamic imports because module evaluation order becomes more fragmented. A lazily loaded boundary can shift when the runtime initializes certain client modules, increasing the chance that the HMR event and router setup happen out of order.
In practical terms, the warning usually means:
- Turbopack dev runtime is alive and sending HMR payloads.
- Next.js Pages Router bootstrap has not fully registered the router on window.next.
- A dev-only handler is reading router state too early.
This is why the bug often reproduces only in development, only with Turbopack, and often disappears in production builds or when falling back to webpack.
Step-by-Step Solution
The safest approach is to treat this as a development-runtime incompatibility and reduce the timing conflict rather than trying to read window.next.router directly.
1. Confirm the issue is limited to Turbopack dev mode
Run the app in normal dev mode and then in a production build. If the warning only appears with Turbopack HMR, you are dealing with this specific runtime race.
npm run dev -- --turbo
npm run build
npm run start
If production is clean, avoid implementing invasive application-level workarounds for a dev-only warning unless it breaks local development.
2. Remove any direct dependency on window.next.router
If your code, custom scripts, or debug helpers read window.next.router, replace that with supported APIs. In the Pages Router, use next/router.
import { useRouter } from 'next/router'
export default function ExamplePage() {
const router = useRouter()
return (
<button onClick={() => router.push('/about')}>
Go to about
</button>
)
}
For class components or non-hook usage:
import Router from 'next/router'
export function goToAbout() {
Router.push('/about')
}
This avoids relying on a global object that may not exist yet during HMR event processing.
3. Guard browser globals in development-only code
If you maintain custom client bootstrap code, analytics hooks, or dev instrumentation, protect any router access behind existence checks.
if (
typeof window !== 'undefined' &&
window.next &&
window.next.router
) {
const currentPath = window.next.router.pathname
console.log(currentPath)
}
This does not fix Turbopack internals, but it prevents your own code from amplifying the warning or throwing secondary errors.
4. Avoid eager router access during module evaluation
A common trigger is reading router data at the top level of a module, especially in files involved in dynamic import chains. Move router-dependent logic into component lifecycle or event handlers.
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function ClientOnlyTracker() {
const router = useRouter()
useEffect(() => {
if (!router.isReady) return
console.log('Route is ready:', router.asPath)
}, [router.isReady, router.asPath])
return null
}
This pattern is especially important when the page depends on code splitting.
5. Test without problematic dynamic boundaries
If your reproduction includes next/dynamic, temporarily inline the component to confirm the timing sensitivity.
// Before
import dynamic from 'next/dynamic'
const Widget = dynamic(() => import('../components/Widget'))
export default function Page() {
return <Widget />
}
// Temporary diagnostic version
import Widget from '../components/Widget'
export default function Page() {
return <Widget />
}
If the warning disappears, the issue is strongly tied to how Turbopack orders HMR updates around lazy chunks.
6. Upgrade Next.js to the latest canary or stable patch
Because this behavior is rooted in framework runtime code, the most effective long-term fix is often a framework update. Check the Next.js issue tracker and release notes for patches related to Turbopack, HMR, or Page Router.
npm install next@latest
# or, if testing an upstream fix
npm install next@canary
Then remove .next and restart the dev server.
rm -rf .next
npm run dev -- --turbo
7. Use webpack for Pages Router development if the warning is disruptive
If your team depends on a clean local console and the upstream fix is not yet available, the pragmatic workaround is to avoid Turbopack for this project until the bug is resolved.
npm run dev
This is a valid short-term mitigation when working on a Pages Router codebase that is sensitive to HMR timing issues.
8. If needed, isolate the router-dependent code behind a client-ready gate
For components that must wait until the router is stable, add a small readiness gate.
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
export default function SafeRouterConsumer() {
const router = useRouter()
const [ready, setReady] = useState(false)
useEffect(() => {
if (router.isReady) setReady(true)
}, [router.isReady])
if (!ready) return null
return <p>Current route: {router.asPath}</p>
}
This reduces the chance of consuming router state before initialization completes.
Common Edge Cases
- Mixing App Router and Page Router patterns: Importing hooks from next/navigation inside Pages Router files can create confusing runtime behavior. In the Pages Router, prefer next/router.
- Top-level browser access: Any module that references window, document, or window.next during import time can fire too early during HMR.
- Custom _app or _document modifications: Aggressive client bootstrap logic in pages/_app can interact badly with hot updates if it assumes router availability immediately.
- Third-party analytics or progress-bar packages: Libraries that hook directly into router events may still rely on old globals. Audit them if the warning persists after your own code is clean.
- Stale cache artifacts: Turbopack and Next.js dev caches can preserve bad state. Deleting .next is often necessary after upgrades or config changes.
- False assumption that the warning is harmless: Sometimes it is only noise, but if you also see broken navigation, missing route updates, or failed fast refresh, treat it as a real runtime bug and test with webpack to compare behavior.
FAQ
Is this a production bug?
Usually no. This warning is typically tied to Turbopack development HMR. If next build and next start work correctly, the issue is most likely limited to the dev runtime.
Why does dynamic import make the warning easier to reproduce?
Dynamic imports change when modules are loaded and evaluated. That can expose race conditions where HMR events arrive before the Page Router has attached itself to window.next.router.
What is the best workaround until Next.js fixes it upstream?
The best workaround is to stop depending on window.next.router, guard client-only code carefully, upgrade Next.js, and use webpack dev mode temporarily if Turbopack warnings are blocking development.
Bottom line: this bug is caused by a dev-time initialization race between the Turbopack HMR client and the Pages Router global router setup. The durable fix is to rely on supported router APIs, avoid early global access, and update to a framework version where the runtime ordering bug is patched.