All guidesMore providers: databases, auth, hosting, email and payments
On this page

Storybook + Next.js error

Storybook

invariant expected app router to be mounted

By Ohad Krispin, founder of VibeRaven · LinkedIn

"invariant expected app router to be mounted" is thrown by useRouter from next/navigation when no App Router context sits above the component. Next.js supplies that context in the app. Storybook renders only the story, so a component that calls useRouter fails the moment the story renders. In Storybook's Next.js frameworks (@storybook/nextjs and @storybook/nextjs-vite), the router decorator adds the App Router context only when the nextjs.appDirectory parameter is true; otherwise it wraps the story in the Pages Router provider. Set parameters.nextjs.appDirectory to true in .storybook/preview for every story, or in one story, and reload. The error shows up in Storybook, not in next build, so a green Vercel deploy does not catch it. Reproduced with next 16.4.3 on 2026-10-31.

The message, and the sibling message for next/router

The text comes from useRouter in next/navigation (packages/next/src/client/components/navigation.ts), which reads the app router context and throws when it is empty. The Pages Router hook has its own message. In a run with next 16.4.3 on 2026-10-31, rendering a component outside a Next.js app printed both throws, while usePathname and useSearchParams returned null instead of throwing.

next/navigation useRouter  THROWS: invariant expected app router to be mounted
next/router useRouter      THROWS: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted
usePathname / useSearchParams  null | null

What Storybook does with nextjs.appDirectory

The Storybook Next.js docs say next/navigation can only be used in components and pages in the app directory, and that stories whose components import it need nextjs.appDirectory set to true. If every page in the project is in the app directory, set it once in .storybook/preview. The decorator in Storybook's source (frameworks/nextjs/src/routing/decorator.tsx) reads parameters.nextjs.appDirectory, defaults it to false, and picks one provider per story: the App Router provider when it is true, the Pages Router provider when it is not.

// .storybook/preview.ts
const preview = {
  parameters: {
    nextjs: {
      appDirectory: true,
    },
  },
};
export default preview;

How do I set the pathname or params for one story?

The nextjs.navigation parameter is the router object passed to the next/navigation context. In the decorator it is merged over a default of pathname "/" and an empty query. For useParams, the docs say to give a segments array in which each element is an array of two strings.

export const Settings = {
  parameters: {
    nextjs: {
      appDirectory: true,
      navigation: {
        pathname: '/settings/profile',
      },
    },
  },
};

Checklist

How to fix it, then check the fix

  1. 01
    Find the component that calls the hook and which module it imports from:
    grep -rnE "from 'next/(navigation|router)'" src app components --include=*.tsx --include=*.ts
  2. 02If the app uses the app directory, set parameters.nextjs.appDirectory to true in .storybook/preview. If only some stories use next/navigation, set it in those stories instead, because the decorator uses one provider per story.
  3. 03If the message is "NextRouter was not mounted" instead, a component imports useRouter from next/router. In the app directory, change it to next/navigation, as the Next.js docs say.
  4. 04For a component that reads the URL, add nextjs.navigation with the pathname (and segments for useParams) the story needs.
  5. 05Restart Storybook, reload the story and confirm the canvas renders with no error overlay. Then run the story tests you use in CI.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Why does next build pass on Vercel while the story fails?

The hook throws when the component renders, and a Next.js page renders inside the App Router context. A story renders outside it, so only Storybook and tests that render the story hit the error.

Which package do I use, @storybook/nextjs or @storybook/nextjs-vite?

Both ship the same routing decorator, so the nextjs.appDirectory parameter works the same in each. The Storybook docs list the Vite-based package on its own page; follow the page that matches the package in your package.json.

Should I wrap the story in AppRouterContext myself?

Not when you use a Storybook Next.js framework. Its decorator already provides the context when nextjs.appDirectory is true.