Skip to content

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:

  1. Using the App Router (new /app directory)
  2. Accessing useRouter from next/navigation
  3. The Next.js router context is unavailable or improperly initialized

The error originates from this code in Next.js:

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:

jsx
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:

jsx
// ❌ Problem: Body is wrapped in provider
export default function RootLayout({ children }) {
  return (
    <html>
      <AuthProvider>
        <body>{children}</body> {/* Context wraps body */}
      </AuthProvider>
    </html>
  );
}
jsx
// ✅ 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:

bash
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:

jsx
// 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:

js
jest.mock('next/navigation'); // Add to test setup file

For Storybook (@storybook/nextjs):

js
const preview = {
  parameters: {
    nextjs: { appDirectory: true }, // Enable App Router
  },
};

5. Client-Component Migration

Shared components between /app and /pages may need 'use client' directive:

jsx
'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:

  1. The App Router is properly initialized
  2. Required DOM elements (<html>, <body>) exist in root layout
  3. 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.

  1. Create a proper app/layout.js with <html> and <body>
  2. Move existing layout logic inside the <body> tag
  3. Verify all useRouter imports use next/navigation
  4. Check for parallel route requirements and missing default.js files
  5. Test functionality progressively using Next.js's migration guide

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.