Next.js App Router Error: Invariant Expected App Router to Be Mounted
Problem Statement
When migrating from Next.js 12 to 13 using the new /app
directory structure, you may encounter the error:
Uncaught Error: invariant expected app router to be mounted
This error typically occurs when:
- Using the App Router (new
/app
directory) - Accessing
useRouter
fromnext/navigation
- The Next.js router context is unavailable or improperly initialized
The error originates from this code in Next.js:
function useRouter() {
const router = useContext(AppRouterContext);
if (router === null) {
throw new Error('invariant expected app router to be mounted');
}
return router;
}
Key symptoms:
- Error occurs only in
/app
directory components - Router functionality is broken
- Issue doesn't affect
/pages
directory components - Error appears during migration from Next.js 12 to 13
Solutions
1. Add Required HTML/Body Tags to Root Layout
The most common solution involves fixing your root layout file (app/layout.tsx
). Next.js requires a specific HTML structure:
export default function RootLayout({ children }: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
Documentation Requirement:
"The root layout must define
<html>
and<body>
tags since Next.js does not automatically create them."
– Next.js Layouts Documentation
WARNING
Your layout file must directly return <html>
and <body>
tags without any wrapping components:
// ❌ Problem: Body is wrapped in provider
export default function RootLayout({ children }) {
return (
<html>
<AuthProvider>
<body>{children}</body> {/* Context wraps body */}
</AuthProvider>
</html>
);
}
// ✅ Solution: Keep body tag clean
export default function RootLayout({ children }) {
return (
<html>
<body>
<AuthProvider>{children}</AuthProvider> {/* Inside body */}
</body>
</html>
);
}
2. Handle Parallel Routes Correctly
When using parallel routes, ensure a default.js
file exists at the root of each slot to provide fallback content:
app/
layout.js
@sidebar/
default.js # Required even if empty!
page.js
3. Fix Router Import Conflicts
Use consistent router imports based on your routing strategy:
// App Router (/app directory)
import { useRouter } from 'next/navigation';
// Pages Router (/pages directory)
import { useRouter } from 'next/router';
4. Testing Environment Fixes
When using testing tools like Jest:
jest.mock('next/navigation'); // Add to test setup file
For Storybook (@storybook/nextjs
):
const preview = {
parameters: {
nextjs: { appDirectory: true }, // Enable App Router
},
};
5. Client-Component Migration
Shared components between /app
and /pages
may need 'use client'
directive:
'use client'; // Add to top of shared component file
import { useRouter } from 'next/navigation';
function SharedComponent() {
const router = useRouter();
// ...
}
Additional Considerations
- Do not manipulate DOM directly: Avoid
document.querySelector
in layout components - Avoid client-side rendering for root layout: Keep
app/layout.js
as a Server Component by default - Check file placement: Component-specific loading states (
loading.js
) should reside in page directories, not app root
Explanation of Underlying Cause
The useRouter
hook relies on the AppRouterContext
which is only mounted when:
- The App Router is properly initialized
- Required DOM elements (
<html>
,<body>
) exist in root layout - Components are correctly placed in the App Router structure
When migrating from Pages Router to App Router, the structural requirements change significantly. The App Router requires explicit HTML structure declarations that weren't necessary in previous versions, leading to this error when omitted.
Recommended Migration Strategy
- Create a proper
app/layout.js
with<html>
and<body>
- Move existing layout logic inside the
<body>
tag - Verify all
useRouter
imports usenext/navigation
- Check for parallel route requirements and missing
default.js
files - Test functionality progressively using Next.js's migration guide
Official Resources
By addressing these structural requirements and import patterns, you can resolve the "invariant expected app router to be mounted" error and successfully migrate to Next.js 13's App Router.