Navigation errors in Next.js pages directory typically occur when using the wrong useRouter import or attempting to use App Router features in pages. This happens because Next.js has two routing systems with different APIs and imports, and mixing them causes runtime failures.
Next.js provides two different routing systems: the Pages Router (the traditional file-system based router) and the App Router (introduced in Next.js 13). Each has its own useRouter hook with different APIs and behaviors. The "NextRouter was not mounted" error occurs when you try to use the useRouter hook from pages in a context where it isn't available, or when you're mixing imports between the two routing systems. The pages directory expects the useRouter hook from 'next/router', while the app directory uses useRouter from 'next/navigation'. This error is Next.js's way of telling you that the Router context hasn't been initialized for the current component. Since useRouter is a hook that depends on React's Context API, it requires the Router provider to be present in the component tree, which is only set up properly when you're using the correct router import for your routing system.
The Pages Router requires useRouter from 'next/router', not from 'next/navigation'. Check all your imports:
// ❌ Wrong - This is for App Router
import { useRouter } from 'next/navigation';
// ✅ Correct - This is for Pages Router
import { useRouter } from 'next/router';
function MyPage() {
const router = useRouter();
// Now you can use router.push(), router.pathname, router.query, etc.
return <div>Current path: {router.pathname}</div>;
}
export default MyPage;Make sure all files in your pages directory use 'next/router' and never use 'next/navigation' unless you're in the app directory.
The Pages Router requires an _app.js or _app.tsx file at the root of your pages directory. This file initializes the Router context for all pages:
// pages/_app.js
import type { AppProps } from 'next/app';
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default MyApp;Without this file, the Router context won't be initialized, and useRouter will fail. If you have a custom _app.js, make sure it's properly exporting the component as the default export.
The useRouter hook only works when called from within a component tree that is rendered by Next.js pages. You cannot use it in:
- Standalone utility files
- Components used outside of page rendering
- getServerSideProps or getStaticProps functions
// ❌ Wrong - useRouter won't work in utility file
// lib/utils.ts
export function handleNavigation() {
const router = useRouter(); // Error!
}
// ✅ Correct - useRouter works inside page components
// pages/my-page.js
import { useRouter } from 'next/router';
export default function MyPage() {
const router = useRouter();
const handleClick = () => {
router.push('/other-page');
};
return <button onClick={handleClick}>Navigate</button>;
}If you need router functionality in utility files, pass the router instance as a parameter from the component that has access to it.
If your project has both app and pages directories, Next.js may become confused about which routing system is primary. To fix this:
Option 1: Use only Pages Router - Delete the app directory if you're not using App Router features:
# If you're committed to Pages Router
rm -rf app/Option 2: Use only App Router - Migrate all pages to the app directory and delete pages directory.
Option 3: Migration with next/compat/router - If gradually migrating, use the compatibility router:
import { useRouter } from 'next/compat/router';
function MyComponent() {
const router = useRouter();
return <div>{router?.pathname}</div>;
}Mixing the two systems in one project causes unpredictable behavior and context initialization errors.
useRouter must be called inside a React functional component, not at the module level or in class components:
// ❌ Wrong - Called at module level
import { useRouter } from 'next/router';
const router = useRouter(); // Error!
function MyPage() {
return <div>{router.pathname}</div>;
}
// ✅ Correct - Called inside component
import { useRouter } from 'next/router';
function MyPage() {
const router = useRouter();
return <div>{router.pathname}</div>;
}
// ✅ Also correct - In class component using withRouter wrapper
import { withRouter } from 'next/router';
import React from 'react';
class MyPage extends React.Component {
render() {
return <div>{this.props.router.pathname}</div>;
}
}
export default withRouter(MyPage);When a page first loads, the router may not be fully ready. Check router.isReady before accessing router.query, router.pathname, or other properties:
import { useRouter } from 'next/router';
function MyPage() {
const router = useRouter();
// ❌ Wrong - router.query might be empty before ready
const id = router.query.id;
// ✅ Correct - Check isReady first
if (!router.isReady) {
return <div>Loading...</div>;
}
const id = router.query.id;
return <div>Product ID: {id}</div>;
}
export default MyPage;Alternatively, use useEffect to handle router-dependent logic:
useEffect(() => {
if (router.isReady) {
// Access router.query safely here
const id = router.query.id;
}
}, [router.isReady, router.query]);After making changes, rebuild your Next.js project and restart the development server:
# Stop the current dev server (Ctrl+C)
# Clean build artifacts
rm -rf .next
# Restart dev server
npm run dev
# Or for production build
npm run build
npm run startSometimes the issue is just stale build cache. A clean rebuild and server restart often resolves the problem. If you made changes to _app.js, restart is essential for the new Router context provider to take effect.
The Router context in Next.js pages directory is provided by the _app component and uses React's Context API internally. When you call useRouter, it accesses this context. If the context is undefined (not mounted), it means either the _app.js file is missing, you're using the wrong import, or you're calling useRouter outside of a component tree that has the context provider.
For gradual migrations from Pages to App Router, Next.js provides next/compat/router which wraps the useRouter hook to work in both contexts. This allows you to incrementally adopt App Router features while maintaining Pages Router in some parts of your application.
When working with dynamic routes in pages directory (like pages/post/[id].js), the router.query object is populated asynchronously after the page mounts. Use router.isReady to safely determine when dynamic route segments are available. This is different from getStaticProps getStaticPaths which receive params synchronously.
In Next.js 13+, if you have both directories, the framework tries to intelligently route requests, but this can be fragile. The app directory takes precedence for overlapping routes. Use explicit routing strategies: commit fully to App Router or keep Pages Router only.
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React