From 210e45ce93a2a0113f593ad61e9bd53592558ce4 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 31 Oct 2025 08:58:44 +0100 Subject: [PATCH 01/11] Add documentation for Storybook with Next.js (Vite) framework and update Next.js (Webpack) section to recommend Vite migration --- docs/get-started/frameworks/nextjs-vite.mdx | 938 ++++++++++++++++++++ docs/get-started/frameworks/nextjs.mdx | 75 +- docs/releases/upgrading.mdx | 35 + 3 files changed, 1004 insertions(+), 44 deletions(-) create mode 100644 docs/get-started/frameworks/nextjs-vite.mdx diff --git a/docs/get-started/frameworks/nextjs-vite.mdx b/docs/get-started/frameworks/nextjs-vite.mdx new file mode 100644 index 000000000000..74d109ca46ae --- /dev/null +++ b/docs/get-started/frameworks/nextjs-vite.mdx @@ -0,0 +1,938 @@ +--- +title: Storybook for Next.js (Vite) +hideRendererSelector: true +sidebar: + order: 2 + title: Next.js (Vite) +--- + +Storybook for Next.js (Vite) is the **recommended** [framework](../../contribute/framework.mdx) for developing and testing UI components in isolation for [Next.js](https://nextjs.org/) applications. It uses [Vite](https://vitejs.dev/) for faster builds and better performance. It includes: + +* πŸ”€ Routing +* πŸ–Ό Image optimization +* ‡️ Absolute imports +* 🎨 Styling +* ⚑ Vite-powered builds +* πŸ’« and more! + +## Requirements + +* Next.js β‰₯ 14.1 + +## Getting started + +### In a project without Storybook + +When you run `storybook init` in your Next.js project, Storybook will automatically detect your project and select the `@storybook/nextjs-vite` framework **unless** your project has custom Webpack or Babel configurations that may be incompatible with Vite. + +Follow the prompts after running this command in your Next.js project's root directory: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +[More on getting started with Storybook.](../install.mdx) + + + +If your project has a custom `webpack.config.js` or `.babelrc` file, `storybook init` will prompt you to choose between: + +- **`@storybook/nextjs-vite`** (recommended) - Faster, more modern, supports latest testing features +- **`@storybook/nextjs`** (Webpack 5) - Better compatibility with custom Webpack/Babel configurations + +Choose `nextjs-vite` if you're willing to migrate your custom configurations to Vite. Choose `nextjs` (Webpack 5) if you need to keep your existing Webpack/Babel setup. + + + +### In a project with Storybook + +This framework is designed to work with Storybook 10+. If you're not already using v10, upgrade with this command: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +#### Automatic migration + +When running the `upgrade` command above, you should get a prompt asking you to migrate to `@storybook/nextjs-vite`, which should handle everything for you. In case that auto-migration does not work for your project, refer to the manual migration below. + +You can also use the [`nextjs-to-nextjs-vite` automigration](../../releases/upgrading.mdx#nextjs-to-nextjs-vite-automigration) to migrate from the Webpack-based `@storybook/nextjs` framework to this Vite-based framework. + +#### Manual migration + +First, install the framework: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +Then, update your `.storybook/main.js|ts` to change the framework property: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + + + +If your Storybook configuration contains custom Webpack operations in [`webpackFinal`](../../api/main-config/main-config-webpack-final.mdx), you will likely need to create equivalents in [`viteFinal`](../../api/main-config/main-config-vite-final.mdx). + +For more information, see the [Vite builder documentation](../../builders/vite.mdx#migrating-from-webpack). + + + +Finally, if you were using Storybook plugins to integrate with Next.js, those are no longer necessary when using this framework and can be removed: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +## Run the Setup Wizard + +If all goes well, you should see a setup wizard that will help you get started with Storybook introducing you to the main concepts and features, including how the UI is organized, how to write your first story, and how to test your components' response to various inputs utilizing [controls](../../essentials/controls.mdx). + +![Storybook onboarding](../../_assets/get-started/example-onboarding-wizard.png) + +If you skipped the wizard, you can always run it again by adding the `?path=/onboarding` query parameter to the URL of your Storybook instance, provided that the example stories are still available. + +## Benefits of Vite + +This Vite-based framework offers several advantages over the Webpack-based `@storybook/nextjs` framework: + +* ⚑ **Faster builds** - Vite's build system is significantly faster than Webpack +* πŸ”§ **Modern tooling** - Uses the latest build tools and optimizations +* πŸ§ͺ **Better test support** - Full support for the [Vitest addon](../../writing-tests/integrations/vitest-addon.mdx) and other testing features +* πŸ“¦ **Simpler configuration** - No need for Babel or complex Webpack configurations +* 🎯 **Better development experience** - Faster HMR (Hot Module Replacement) and dev server startup + +## Next.js's Image component + +This framework allows you to use Next.js's [next/image](https://nextjs.org/docs/pages/api-reference/components/image) with no configuration. + +### Local images + +[Local images](https://nextjs.org/docs/pages/building-your-application/optimizing/images#local-images) are supported. + +```jsx title="index.jsx" +import Image from 'next/image'; +import profilePic from '../public/me.png'; + +function Home() { + return ( + <> +

My Homepage

+ Picture of the author +

Welcome to my homepage!

+ + ); +} +``` + +### Remote images + +[Remote images](https://nextjs.org/docs/pages/building-your-application/optimizing/images#remote-images) are also supported. + +```jsx title="index.jsx" +import Image from 'next/image'; + +export default function Home() { + return ( + <> +

My Homepage

+ Picture of the author +

Welcome to my homepage!

+ + ); +} +``` + +## Next.js font optimization + +[next/font](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts) is partially supported in Storybook. The packages `next/font/google` and `next/font/local` are supported. + +### `next/font/google` + +You don't have to do anything. `next/font/google` is supported out of the box. + +### `next/font/local` + +For local fonts you have to define the [src](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts#local-fonts) property. +The path is relative to the directory where the font loader function is called. + +If the following component defines your localFont like this: + +```js title="src/components/MyComponent.js" +import localFont from 'next/font/local'; + +const localRubikStorm = localFont({ src: './fonts/RubikStorm-Regular.ttf' }); +``` + +The Vite-based framework automatically handles font path mapping, so you don't need to configure `staticDirs` for fonts like you would with the Webpack-based framework. + +### Not supported features of `next/font` + +The following features are not supported (yet). Support for these features might be planned for the future: + +* [Support font loaders configuration in next.config.js](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts#local-fonts) +* [fallback](https://nextjs.org/docs/pages/api-reference/components/font#fallback) option +* [adjustFontFallback](https://nextjs.org/docs/pages/api-reference/components/font#adjustfontfallback) option +* [preload](https://nextjs.org/docs/pages/api-reference/components/font#preload) option gets ignored. Storybook handles Font loading its own way. +* [display](https://nextjs.org/docs/pages/api-reference/components/font#display) option gets ignored. All fonts are loaded with display set to "block" to make Storybook load the font properly. + +### Mocking fonts during testing + +Occasionally fetching fonts from Google may fail as part of your Storybook build step. It is highly recommended to mock these requests, as those failures can cause your pipeline to fail as well. Next.js [supports mocking fonts](https://github.com/vercel/next.js/blob/725ddc7371f80cca273779d37f961c3e20356f95/packages/font/src/google/fetch-css-from-google-fonts.ts#L36) via a JavaScript module located where the env var `NEXT_FONT_GOOGLE_MOCKED_RESPONSES` references. + +For example, using [GitHub Actions](https://www.chromatic.com/docs/github-actions): + +```yaml title=".github/workflows/ci.yml" +- uses: chromaui/action@latest + env: + #πŸ‘‡ the location of mocked fonts to use + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: ${{ github.workspace }}/mocked-google-fonts.js + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} +``` + +Your mocked fonts will look something like this: + +```js title="mocked-google-fonts.js" +//πŸ‘‡ Mocked responses of google fonts with the URL as the key +module.exports = { + 'https://fonts.googleapis.com/css?family=Inter:wght@400;500;600;800&display=block': ` + /* cyrillic-ext */ + @font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiJ-Ek-_EeAmM.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + } + /* more font declarations go here */ + /* latin */ + @font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiJ-Ek-_EeA.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + }`, +}; +``` + +## Next.js routing + +[Next.js's router](https://nextjs.org/docs/pages/building-your-application/routing) is automatically stubbed for you so that when the router is interacted with, all of its interactions are automatically logged to the [Actions panel](../../essentials/actions.mdx). + + + +You should only use `next/router` in the `pages` directory. In the `app` directory, it is necessary to use `next/navigation`. + + + +### Overriding defaults + +Per-story overrides can be done by adding a `nextjs.router` property onto the story [parameters](../../writing-stories/parameters.mdx). The framework will shallowly merge whatever you put here into the router. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + + + +These overrides can also be applied to [all stories for a component](../../api/parameters.mdx#meta-parameters) or [all stories in your project](../../api/parameters.mdx#project-parameters). Standard [parameter inheritance](../../api/parameters.mdx#parameter-inheritance) rules apply. + + + +### Default router + +The default values on the stubbed router are as follows (see [globals](../../essentials/toolbars-and-globals.mdx#globals) for more details on how globals work). + +```ts +// Default router +const defaultRouter = { + // The locale should be configured globally: https://storybook.js.org/docs/essentials/toolbars-and-globals#globals + locale: globals?.locale, + asPath: '/', + basePath: '/', + isFallback: false, + isLocaleDomain: false, + isReady: true, + isPreview: false, + route: '/', + pathname: '/', + query: {}, +}; +``` + +Additionally, the [`router` object](https://nextjs.org/docs/pages/api-reference/functions/use-router#router-object) contains all of the original methods (such as `push()`, `replace()`, etc.) as mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html). + +To override these defaults, you can use [parameters](../../writing-stories/parameters.mdx) and [`beforeEach`](../../writing-stories/mocking-data-and-modules/mocking-modules.mdx#setting-up-and-cleaning-up): + +```ts title=".storybook/preview.js|ts" +import type { Preview } from '@storybook/nextjs-vite'; + +// πŸ‘‡ Must include the `.mock` portion of filename to have mocks typed correctly +import { getRouter } from "@storybook/nextjs-vite/router.mock"; + +const preview: Preview = { + parameters: { + nextjs: { + // πŸ‘‡ Override the default router properties + router: { + basePath: '/app/', + }, + }, + }, + async beforeEach() { + // πŸ‘‡ Manipulate the default router method mocks + getRouter().push.mockImplementation(() => { + /* ... */ + }); + }, +}; +``` + +## Next.js navigation + + + +Please note that [`next/navigation`](https://nextjs.org/docs/app/building-your-application/routing) can only be used in components/pages in the `app` directory. + + + +### Set `nextjs.appDirectory` to `true` + +If your story imports components that use `next/navigation`, you need to set the parameter `nextjs.appDirectory` to `true` in for that component's stories: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +If your Next.js project uses the `app` directory for every page (in other words, it does not have a `pages` directory), you can set the parameter `nextjs.appDirectory` to `true` in the [`.storybook/preview.js|ts`](../../configure/index.mdx#configure-story-rendering) file to apply it to all stories. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +### Overriding defaults + +Per-story overrides can be done by adding a `nextjs.navigation` property onto the story [parameters](../../writing-stories/parameters.mdx). The framework will shallowly merge whatever you put here into the router. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + + + +These overrides can also be applied to [all stories for a component](../../api/parameters.mdx#meta-parameters) or [all stories in your project](../../api/parameters.mdx#project-parameters). Standard [parameter inheritance](../../api/parameters.mdx#parameter-inheritance) rules apply. + + + +### `useSelectedLayoutSegment`, `useSelectedLayoutSegments`, and `useParams` hooks + +The `useSelectedLayoutSegment`, `useSelectedLayoutSegments`, and `useParams` hooks are supported in Storybook. You have to set the `nextjs.navigation.segments` parameter to return the segments or the params you want to use. + +{/* prettier-ignore-start */} + + + +With the above configuration, the component rendered in the stories would receive the following values from the hooks: + +```js title="NavigationBasedComponent.js" +import { useSelectedLayoutSegment, useSelectedLayoutSegments, useParams } from 'next/navigation'; + +export default function NavigationBasedComponent() { + const segment = useSelectedLayoutSegment(); // dashboard + const segments = useSelectedLayoutSegments(); // ["dashboard", "analytics"] + const params = useParams(); // {} + // ... +} +``` + +{/* prettier-ignore-end */} + +To use `useParams`, you have to use a segments array where each element is an array containing two strings. The first string is the param key and the second string is the param value. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +With the above configuration, the component rendered in the stories would receive the following values from the hooks: + +```js title="ParamsBasedComponent.js" +import { useSelectedLayoutSegment, useSelectedLayoutSegments, useParams } from 'next/navigation'; + +export default function ParamsBasedComponent() { + const segment = useSelectedLayoutSegment(); // hello + const segments = useSelectedLayoutSegments(); // ["hello", "nextjs"] + const params = useParams(); // { slug: "hello", framework: "nextjs" } + ... +} +``` + + + +These overrides can also be applied to [a single story](../../api/parameters.mdx#story-parameters) or [all stories in your project](../../api/parameters.mdx#project-parameters). Standard [parameter inheritance](../../api/parameters.mdx#parameter-inheritance) rules apply. + + + +The default value of `nextjs.navigation.segments` is `[]` if not set. + +### Default navigation context + +The default values on the stubbed navigation context are as follows: + +```ts +// Default navigation context +const defaultNavigationContext = { + pathname: '/', + query: {}, +}; +``` + +Additionally, the [`router` object](https://nextjs.org/docs/app/api-reference/functions/use-router#userouter) contains all of the original methods (such as `push()`, `replace()`, etc.) as mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html). + +To override these defaults, you can use [parameters](../../writing-stories/parameters.mdx) and [`beforeEach`](../../writing-stories/mocking-data-and-modules/mocking-modules.mdx#setting-up-and-cleaning-up): + +```ts title=".storybook/preview.js|ts" +import type { Preview } from '@storybook/nextjs-vite'; + +// πŸ‘‡ Must include the `.mock` portion of filename to have mocks typed correctly +import { getRouter } from '@storybook/nextjs-vite/navigation.mock'; + +const preview: Preview = { + parameters: { + nextjs: { + // πŸ‘‡ Override the default navigation properties + navigation: { + pathname: '/app/', + }, + }, + }, + async beforeEach() { + // πŸ‘‡ Manipulate the default navigation method mocks + getRouter().push.mockImplementation(() => { + /* ... */ + }); + }, +}; +``` + +## Next.js Head + +[`next/head`](https://nextjs.org/docs/pages/api-reference/components/head) is supported out of the box. You can use it in your stories like you would in your Next.js application. Please keep in mind, that the Head `children` are placed into the head element of the iframe that Storybook uses to render your stories. + +## Sass/Scss + +[Global Sass/Scss stylesheets](https://nextjs.org/docs/pages/building-your-application/styling/sass) are supported without any additional configuration as well. Just import them into [`.storybook/preview.js|ts`](../../configure/index.mdx#configure-story-rendering) + +```js title=".storybook/preview.js|ts" +import '../styles/globals.scss'; +``` + +This will automatically include any of your [custom Sass configurations](https://nextjs.org/docs/pages/building-your-application/styling/sass#customizing-sass-options) in your `next.config.js` file. + +```js title="next.config.js" +import * as path from 'path'; + +export default { + // Any options here are included in Sass compilation for your stories + sassOptions: { + includePaths: [path.join(process.cwd(), 'styles')], + }, +}; +``` + +## CSS/Sass/Scss Modules + +[CSS modules](https://nextjs.org/docs/pages/building-your-application/styling/css-modules) work as expected. + +```jsx title="src/components/Button.jsx" +// This import will work in Storybook +import styles from './Button.module.css'; +// Sass/Scss is also supported +// import styles from './Button.module.scss' +// import styles from './Button.module.sass' + +export function Button() { + return ( + + ); +} +``` + +## PostCSS + +Next.js lets you [customize PostCSS config](https://nextjs.org/docs/pages/building-your-application/configuring/post-css). Thus this framework will automatically handle your PostCSS config for you. + +This allows for cool things like zero-config Tailwind! (See [Next.js' example](https://github.com/vercel/next.js/tree/canary/packages/create-next-app/templates/default-tw)) + +## Absolute imports + +[Absolute imports](https://nextjs.org/docs/pages/building-your-application/configuring/absolute-imports-and-module-aliases#absolute-imports) from the root directory are supported. + +```jsx title="index.jsx|tsx" +// All good! +import Button from 'components/button'; +// Also good! +import styles from 'styles/HomePage.module.css'; + +export default function HomePage() { + return ( + <> +

Hello World

+