Storybook for Next.js
Storybook for Next.js is a framework that makes it easy to develop and test UI components in isolation for Next.js applications. It includes:
- ๐ Routing
- ๐ผ Image optimization
- โคต๏ธ Absolute imports
- ๐จ Styling
- ๐ Webpack & Babel config
- ๐ซ and more!
Requirements
- Next.js โฅ 13.5
- Storybook โฅ 7.0
Getting started
In a project without Storybook
Follow the prompts after running this command in your Next.js project's root directory:
More on getting started with Storybook.
In a project with Storybook
This framework is designed to work with Storybook 7+. If youโre not already using v7, upgrade with this command:
Automatic migration
When running the upgrade
command above, you should get a prompt asking you to migrate to @storybook/nextjs
, which should handle everything for you. In case that auto-migration does not work for your project, refer to the manual migration below.
Manual migration
First, install the framework:
Then, update your .storybook/main.js|ts
to change the framework property:
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:
With Vite
(โ ๏ธ Experimental)
You can use our freshly baked, experimental @storybook/experimental-nextjs-vite
framework, which is based on Vite and removes the need for Webpack and Babel. It supports all of the features documented here.
Using the Next.js framework with Vite requires Next.js 14.1.0 or later.
Then, update your .storybook/main.js|ts
to change the framework property:
If your Storybook configuration contains custom Webpack operations in webpackFinal
, you will likely need to create equivalents in viteFinal
.
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:
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.
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.
Next.js's Image component
This framework allows you to use Next.js's next/image with no configuration.
Local images
Local images are supported.
Remote images
Remote images are also supported.
Next.js font optimization
next/font 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 property. The path is relative to the directory where the font loader function is called.
If the following component defines your localFont like this:
staticDir
mapping
You can safely skip this section if you are using @storybook/experimental-nextjs-vite
instead of @storybook/nextjs
. The Vite-based framework takes care of the mapping automatically.
You have to tell Storybook where the fonts
directory is located, via the staticDirs
configuration. The from
value is relative to the .storybook
directory. The to
value is relative to the execution context of Storybook. Very likely it is the root of your project.
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
- fallback option
- adjustFontFallback option
- preload option gets ignored. Storybook handles Font loading its own way.
- 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 via a JavaScript module located where the env var NEXT_FONT_GOOGLE_MOCKED_RESPONSES
references.
For example, using GitHub Actions:
Your mocked fonts will look something like this:
Next.js routing
Next.js's router is automatically stubbed for you so that when the router is interacted with, all of its interactions are automatically logged to the Actions panel if you have the Storybook actions addon.
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. The framework will shallowly merge whatever you put here into the router.
These overrides can also be applied to all stories for a component or all stories in your project. Standard parameter inheritance rules apply.
Default router
The default values on the stubbed router are as follows (see globals for more details on how globals work).
Additionally, the 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.
To override these defaults, you can use parameters and beforeEach
:
Next.js navigation
Please note that next/navigation
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:
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
file to apply it to all stories.
Overriding defaults
Per-story overrides can be done by adding a nextjs.navigation
property onto the story parameters. The framework will shallowly merge whatever you put here into the router.
These overrides can also be applied to all stories for a component or all stories in your project. Standard 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.
With the above configuration, the component rendered in the stories would receive the following values from the hooks:
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.
With the above configuration, the component rendered in the stories would receive the following values from the hooks:
These overrides can also be applied to a single story or all stories in your project. Standard 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:
Additionally, the 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.
To override these defaults, you can use parameters and beforeEach
:
Next.js Head
next/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 are supported without any additional configuration as well. Just import them into .storybook/preview.js|ts
This will automatically include any of your custom Sass configurations in your next.config.js
file.
CSS/Sass/Scss Modules
CSS modules work as expected.
Styled JSX
The built in CSS-in-JS solution for Next.js is styled-jsx, and this framework supports that out of the box too, zero config.
You can use your own babel config too. This is an example of how you can customize styled-jsx.
PostCSS
Next.js lets you customize PostCSS config. Thus this framework will automatically handle your PostCSS config for you.
This allows for cool things like zero-config Tailwind! (See Next.js' example)
Absolute imports
Absolute imports from the root directory are supported.
Also OK for global styles in .storybook/preview.js|ts
!
Absolute imports cannot be mocked in stories/tests. See the Mocking modules section for more information.
Module aliases
Module aliases are also supported.
Subpath imports
As an alternative to module aliases, you can use subpath imports to import modules. This follows Node package standards and has benefits when mocking modules.
To configure subpath imports, you define the imports
property in your project's package.json
file. This property maps the subpath to the actual file path. The example below configures subpath imports for all modules in the project:
Because subpath imports replace module aliases, you can remove the path aliases from your TypeScript configuration.
Which can then be used like this:
Mocking modules
Components often depend on modules that are imported into the component file. These can be from external packages or internal to your project. When rendering those components in Storybook or testing them, you may want to mock those modules to control and assert their behavior.
Built-in mocked modules
This framework provides mocks for many of Next.js' internal modules:
@storybook/nextjs/cache.mock
@storybook/nextjs/headers.mock
@storybook/nextjs/navigation.mock
@storybook/nextjs/router.mock
Mocking other modules
How you mock other modules in Storybook depends on how you import the module into your component.
With either approach, the first step is to create a mock file. Here's an example of a mock file for a module named session
:
With subpath imports
If you're using subpath imports, you can adjust your configuration to apply conditions so that the mocked module is used inside Storybook. The example below configures subpath imports for four internal modules, which are then mocked in Storybook:
Each subpath must begin with #
, to differentiate it from a regular module path. The #*
entry is a catch-all that maps all subpaths to the root directory.
With module aliases
If you're using module aliases, you can add a Webpack alias to your Storybook configuration to point to the mock file.
Runtime config
Next.js allows for Runtime Configuration which lets you import a handy getConfig
function to get certain configuration defined in your next.config.js
file at runtime.
In the context of Storybook with this framework, you can expect Next.js's Runtime Configuration feature to work just fine.
Note, because Storybook doesn't server render your components, your components will only see what they normally see on the client side (i.e. they won't see serverRuntimeConfig
but will see publicRuntimeConfig
).
For example, consider the following Next.js config:
Calls to getConfig
would return the following object when called within Storybook:
Custom Webpack config
You can safely skip this section if you are using @storybook/experimental-nextjs-vite
instead of @storybook/nextjs
.
The Vite-based Next.js framework does not support Webpack settings.
Next.js comes with a lot of things for free out of the box like Sass support, but sometimes you add custom Webpack config modifications to Next.js. This framework takes care of most of the Webpack modifications you would want to add. If Next.js supports a feature out of the box, then that feature will work out of the box in Storybook. If Next.js doesn't support something out of the box, but makes it easy to configure, then this framework will do the same for that thing for Storybook.
Any Webpack modifications desired for Storybook should be made in .storybook/main.js|ts
.
Note: Not all Webpack modifications are copy/paste-able between next.config.js
and .storybook/main.js|ts
. It is recommended to do your research on how to properly make your modification to Storybook's Webpack config and on how Webpack works.
Below is an example of how to add SVGR support to Storybook with this framework.
Typescript
Storybook handles most Typescript configurations, but this framework adds additional support for Next.js's support for Absolute Imports and Module path aliases. In short, it takes into account your tsconfig.json
's baseUrl and paths. Thus, a tsconfig.json
like the one below would work out of the box.
React Server Components (RSC)
(โ ๏ธ Experimental)
If your app uses React Server Components (RSC), Storybook can render them in stories in the browser.
To enable this set the experimentalRSC
feature flag in your .storybook/main.js|ts
config:
Setting this flag automatically wraps your story in a Suspense wrapper, which is able to render asynchronous components in NextJS's version of React.
If this wrapper causes problems in any of your existing stories, you can selectively disable it using the react.rsc
parameter at the global/component/story level:
Note that wrapping your server components in Suspense does not help if your server components access server-side resources like the file system or Node-specific libraries. To work around this, you'll need to mock out your data access layer using Webpack aliases or an addon like storybook-addon-module-mock.
If your server components access data via the network, we recommend using the MSW Storybook Addon to mock network requests.
In the future we will provide better mocking support in Storybook and support for Server Actions.
Notes for Yarn v2 and v3 users
If you're using Yarn v2 or v3, you may run into issues where Storybook can't resolve style-loader
or css-loader
. For example, you might get errors like:
Module not found: Error: Can't resolve 'css-loader'
Module not found: Error: Can't resolve 'style-loader'
This is because those versions of Yarn have different package resolution rules than Yarn v1.x. If this is the case for you, please install the package directly.
FAQ
Stories for pages/components which fetch data
Next.js pages can fetch data directly within server components in the app
directory, which often include module imports that only run in a node environment. This does not (currently) work within Storybook, because if you import from a Next.js page file containing those node module imports in your stories, your Storybook's Webpack will crash because those modules will not run in a browser. To get around this, you can extract the component in your page file into a separate file and import that pure component in your stories. Or, if that's not feasible for some reason, you can polyfill those modules in your Storybook's webpackFinal
configuration.
Before
After
Statically imported images won't load
Make sure you are treating image imports the same way you treat them when using next/image
in normal development.
Before using this framework, image imports would import the raw path to the image (e.g. 'static/media/stories/assets/logo.svg'
). Now image imports work the "Next.js way", meaning that you now get an object when importing an image. For example:
Therefore, if something in Storybook isn't showing the image properly, make sure you expect the object to be returned from an import instead of only the asset path.
See local images for more detail on how Next.js treats static image imports.
Module not found: Error: Can't resolve package name
You might get this if you're using Yarn v2 or v3. See Notes for Yarn v2 and v3 users for more details.
What if I'm using the Vite builder?
We have introduced experimental Vite builder support. Just install the experimental framework package @storybook/experimental-nextjs-vite
and replace all instances of @storybook/nextjs
with @storybook/experimental-nextjs-vite
.
Error: You are importing avif images, but you don't have sharp installed. You have to install sharp in order to use image optimization features in Next.js.
sharp
is a dependency of Next.js's image optimization feature. If you see this error, you need to install sharp
in your project.
You can refer to the Install sharp
to Use Built-In Image Optimization in the Next.js documentation for more information.
API
Modules
The @storybook/nextjs
ย package exportsย several modules that enableย you to mock Next.js's internal behavior.
@storybook/nextjs/export-mocks
Type: { getPackageAliases: ({ useESM?: boolean }) => void }
getPackageAliases
is a helper for generating the aliases needed to set up portable stories.
@storybook/nextjs/cache.mock
Type: typeof import('next/cache')
This module exports mocked implementations of the next/cache
module's exports. You can use it to create your own mock implementations or assert on mock calls in a story's play function.
@storybook/nextjs/headers.mock
Type: cookies
, headers
and draftMode
from Next.js
This module exports writable mocked implementations of the next/headers
module's exports. You can use it to set up cookies or headers that are read in your story, and to later assert that they have been called.
Next.js's default headers()
export is read-only, but this module exposes methods allowing you to write to the headers:
headers().append(name: string, value: string)
: Appends the value to the header if it exists already.headers().delete(name: string)
: Deletes the headerheaders().set(name: string, value: string)
: Sets the header to the value provided.
For cookies, you can use the existing API to write them. E.g., cookies().set('firstName', 'Jane')
.
Because headers()
, cookies()
and their sub-functions are all mocks you can use any mock utilities in your stories, like headers().getAll.mock.calls
.
@storybook/nextjs/navigation.mock
Type: typeof import('next/navigation') & getRouter: () => ReturnType<typeof import('next/navigation')['useRouter']>
This module exports mocked implementations of the next/navigation
module's exports. It also exports a getRouter
function that returns a mocked version of Next.js's router
object from useRouter
, allowing the properties to be manipulated and asserted on. You can use it mock implementations or assert on mock calls in a story's play function.
@storybook/nextjs/router.mock
Type: typeof import('next/router') & getRouter: () => ReturnType<typeof import('next/router')['useRouter']>
This module exports mocked implementations of the next/router
module's exports. It also exports a getRouter
function that returns a mocked version of Next.js's router
object from useRouter
, allowing the properties to be manipulated and asserted on. You can use it mock implementations or assert on mock calls in a story's play function.
Options
You can pass an options object for additional configuration if needed:
The available options are:
builder
Type: Record<string, any>
Configure options for the framework's builder. For Next.js, available options can be found in the Webpack builder docs.
image
Type: object
Props to pass to every instance of next/image
. See next/image docs for more details.
nextConfigPath
Type: string
The absolute path to the next.config.js
file. This is necessary if you have a custom next.config.js
file that is not in the root directory of your project.
Parameters
This framework contributes the following parameters to Storybook, under the nextjs
namespace:
appDirectory
Type: boolean
Default: false
If your story imports components that use next/navigation
, you need to set the parameter nextjs.appDirectory
to true
. Because this is a parameter, you can apply it to a single story, all stories for a component, or every story in your Storybook. See Next.js Navigation for more details.
navigation
Type:
Default value:
The router object that is passed to the next/navigation
context. See Next.js's navigation docs for more details.
router
Type:
The router object that is passed to the next/router
context. See Next.js's router docs for more details.