# Storybook for Next.js ## Table of Contents - [Supported Features](#supported-features) - [Requirements](#requirements) - [Getting Started](#getting-started) - [In a project without Storybook](#in-a-project-without-storybook) - [In a project with Storybook](#in-a-project-with-storybook) - [Documentation](#documentation) - [Options](#options) - [Next.js's Image Component](#nextjss-image-component) - [Local Images](#local-images) - [Remote Images](#remote-images) - [Optimization](#optimization) - [AVIF](#avif) - [Next.js Routing](#nextjs-routing) - [Overriding defaults](#overriding-defaults) - [Global Defaults](#global-defaults) - [Default Router](#default-router) - [Actions Integration Caveats](#actions-integration-caveats) - [Next.js Navigation](#nextjs-navigation) - [Sass/Scss](#sassscss) - [Css/Sass/Scss Modules](#csssassscss-modules) - [Styled JSX](#styled-jsx) - [Postcss](#postcss) - [Absolute Imports](#absolute-imports) - [Runtime Config](#runtime-config) - [Custom Webpack Config](#custom-webpack-config) - [Typescript](#typescript) - [Notes for Yarn v2 and v3 users](#notes-for-yarn-v2-and-v3-users) - [FAQ](#faq) - [Stories for pages](#stories-for-pages-components-which-fetch-data) - [Statically imported images won't load](#statically-imported-images-wont-load) - [Module not found: Error: Can't resolve [package name]](#module-not-found-error-cant-resolve-package-name) - [Acknowledgements](#acknowledgements) ## Supported Features π [Next.js's Image Component](#nextjss-image-component) π [Next.js Routing (next/router)](#nextjs-routing) π [Next.js Navigation (next/navigation)](#nextjs-navigation) π [Sass/Scss](#sassscss) π [Css/Sass/Scss Modules](#csssassscss-modules) π [Styled JSX](#styled-jsx) π [Postcss](#postcss) π [Absolute Imports](#absolute-imports) π [Runtime Config](#runtime-config) π [Custom Webpack Config](#custom-webpack-config) π [Typescript](#typescript) (already supported out of the box by Storybook) ## Requirements - [Next.js](https://nextjs.org/) >= 12.x - [Storybook](https://storybook.js.org/) >= 7.x ## Getting Started ### In a project without Storybook Follow the prompts after running this command in your Next.js project's root directory: ```bash npx storybook@next init ``` [More on getting started with Storybook](https://storybook.js.org/docs/react/get-started/install) ### 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: ```bash npx storybook@next upgrade --prerelease ``` Install the framework: ```bash yarn install -D @storybook/nextjs@next ``` Update your `main.js` to change the framework property: ```js // .storybook/main.js module.exports = { // ... framework: { // name: '@storybook/react-webpack5', // Remove this name: '@storybook/nextjs', // Add this options: {}, }, }; ``` If you were using Storybook plugins to integrate with Next.js, those are no longer necessary when using this framework and can be removed: ```js // .storybook/main.js module.exports = { // ... addons: [ // ... // These can both be removed // 'storybook-addon-next', // 'storybook-addon-next-router', ], }; ``` ## Documentation ### Options You can be pass an options object for addional configuration if needed. For example: ```js // .storybook/main.js const path = require('path'); module.exports = { // ... framework: { name: '@storybook/nextjs', options: { nextConfigPath: path.resolve(__dirname, '../next.config.js'), }, }, }; ``` - `nextConfigPath`: The absolute path to the `next.config.js` ### Next.js's Image Component [next/image](https://nextjs.org/docs/api-reference/next/image) is [notoriously difficult](https://github.com/vercel/next.js/issues/18393) to get working with Storybook. This framework allows you to use Next.js's `Image` component with no configuration! #### Local Images [Local images](https://nextjs.org/docs/basic-features/image-optimization#local-images) work just fine! Keep in mind that this feature was [only added in Next.js v11](https://nextjs.org/blog/next-11#automatic-size-detection-local-images). ```js import Image from 'next/image'; import profilePic from '../public/me.png'; function Home() { return ( <>
Welcome to my homepage!
> ); } ``` #### Remote Images [Remote images](https://nextjs.org/docs/basic-features/image-optimization#remote-images) also work just fine! ```js import Image from 'next/image'; export default function Home() { return ( <>Welcome to my homepage!
> ); } ``` #### AVIF This format is not supported by this framework yet. Feel free to [open up an issue](https://github.com/storybookjs/storybook/issues) if this is something you want to see. ### Next.js Routing [Next.js's router](https://nextjs.org/docs/routing/introduction) is automatically stubbed for you so that when the router is interacted with, all of its interactions are automatically logged to the Actions ctions panel if you have the [Storybook actions addon](https://storybook.js.org/docs/react/essentials/actions). > When using Next.js 13+, 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](https://storybook.js.org/docs/react/writing-stories/parameters). The framework will shallowly merge whatever you put here into the router. ```js // SomeComponentThatUsesTheRouter.stories.js import SomeComponentThatUsesTheRouter from './SomeComponentThatUsesTheRouter'; export default { component: SomeComponentThatUsesTheRouter, }; // If you have the actions addon, // you can interact with the links and see the route change events there export const Example = { parameters: { nextjs: { router: { path: '/profile/[id]', asPath: '/profile/1', query: { id: '1', }, }, }, }, }; ``` #### Global Defaults Global defaults can be set in [preview.js](https://storybook.js.org/docs/react/configure/overview#configure-story-rendering) and will be shallowly merged with the default router. ```js // .storybook/preview.js export const parameters = { nextjs: { router: { path: '/some-default-path', asPath: '/some-default-path', query: {}, }, }, }; ``` #### Default Router The default values on the stubbed router are as follows (see [globals](https://storybook.js.org/docs/react/essentials/toolbars-and-globals#globals) for more details on how globals work) ```ts const defaultRouter = { push(...args) { action('nextRouter.push')(...args); return Promise.resolve(true); }, replace(...args) { action('nextRouter.replace')(...args); return Promise.resolve(true); }, reload(...args) { action('nextRouter.reload')(...args); }, back(...args) { action('nextRouter.back')(...args); }, forward() { action('nextRouter.forward')(); }, prefetch(...args) { action('nextRouter.prefetch')(...args); return Promise.resolve(); }, beforePopState(...args) { action('nextRouter.beforePopState')(...args); }, events: { on(...args) { action('nextRouter.events.on')(...args); }, off(...args) { action('nextRouter.events.off')(...args); }, emit(...args) { action('nextRouter.events.emit')(...args); }, }, // The locale should be configured [globally](https://storybook.js.org/docs/react/essentials/toolbars-and-globals#globals) locale: globals?.locale, asPath: '/', basePath: '/', isFallback: false, isLocaleDomain: false, isReady: true, isPreview: false, route: '/', pathname: '/', query: {}, }; ``` #### Actions Integration Caveats If you override a function, you lose the automatic actions integration and have to build it out yourself. ```js // .storybook/preview.js export const parameters = { nextjs: { router: { push() { // The default implementation that logs the action into the Actions panel is lost }, }, }, }; ``` Doing this yourself looks something like this (make sure you install the `@storybook/addon-actions` package): ```js // .storybook/preview.js import { action } from '@storybook/addon-actions'; export const parameters = { nextjs: { router: { push(...args) { // Custom logic can go here // This logs to the Actions panel action('nextRouter.push')(...args); // Return whatever you want here return Promise.resolve(true); }, }, }, }; ``` ### Next.js Navigation > Please note that [next/navigation](https://beta.nextjs.org/docs/upgrade-guide#step-5-migrating-routing-hooks) can only be used in components/pages in the `app` directory of Next.js 13+. #### 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 your Story: ```js // SomeComponentThatUsesTheRouter.stories.js import SomeComponentThatUsesTheNavigation from './SomeComponentThatUsesTheNavigation'; export default { component: SomeComponentThatUsesTheNavigation, }; export const Example = { parameters: { nextjs: { appDirectory: true, }, }, }, ``` 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 [preview.js](https://storybook.js.org/docs/react/configure/overview#configure-story-rendering) file to apply it to all stories. ```js // .storybook/preview.js export const parameters = { nextjs: { appDirectory: true, }, }; ``` The parameter `nextjs.appDirectory` defaults to `false` if not set. #### Overriding defaults Per-story overrides can be done by adding a `nextjs.navigation` property onto the story [parameters](https://storybook.js.org/docs/react/writing-stories/parameters). The framework will shallowly merge whatever you put here into the router. ```js // SomeComponentThatUsesTheNavigation.stories.js import SomeComponentThatUsesTheNavigation from './SomeComponentThatUsesTheNavigation'; export default { component: SomeComponentThatUsesTheNavigation, }; // If you have the actions addon, // you can interact with the links and see the route change events there export const Example = { parameters: { nextjs: { appDirectory: true, navigation: { pathname: '/profile', query: { user: '1', }, }, }, }, }; ``` #### Global Defaults Global defaults can be set in [preview.js](https://storybook.js.org/docs/react/configure/overview#configure-story-rendering) and will be shallowly merged with the default router. ```js // .storybook/preview.js export const parameters = { nextjs: { appDirectory: true, navigation: { pathname: '/some-default-path', }, }, }; ``` #### Default Navigation Context The default values on the stubbed navigation context are as follows: ```ts const defaultNavigationContext = { push(...args) { action('nextNavigation.push')(...args); }, replace(...args) { action('nextNavigation.replace')(...args); }, forward(...args) { action('nextNavigation.forward')(...args); }, back(...args) { action('nextNavigation.back')(...args); }, prefetch(...args) { action('nextNavigation.prefetch')(...args); }, refresh: () => { action('nextNavigation.refresh')(); }, pathname: '/', query: {}, }; ``` #### Actions Integration Caveats If you override a function, you lose the automatic action tab integration and have to build it out yourself. ```js // .storybook/preview.js export const parameters = { nextjs: { appDirectory: true, navigation: { push() { // The default implementation that logs the action into the Actions panel is lost }, }, }, }; ``` Doing this yourself looks something like this (make sure you install the `@storybook/addon-actions` package): ```js // .storybook/preview.js import { action } from '@storybook/addon-actions'; export const parameters = { nextjs: { appDirectory: true, navigation: { push(...args) { // Custom logic can go here // This logs to the Actions panel action('nextNavigation.push')(...args); // Return whatever you want here return Promise.resolve(true); }, }, }, }; ``` ### Sass/Scss [Global sass/scss stylesheets](https://nextjs.org/docs/basic-features/built-in-css-support#sass-support) are supported without any additional configuration as well. Just import them into [preview.js](https://storybook.js.org/docs/react/configure/overview#configure-story-rendering) ```js import '../styles/globals.scss'; ``` This will automatically include any of your [custom sass configurations](https://nextjs.org/docs/basic-features/built-in-css-support#customizing-sass-options) in your `next.config.js` file. ```js // next.config.js const path = require('path'); module.exports = { // Any options here are included in Sass compilation for your stories sassOptions: { includePaths: [path.join(__dirname, 'styles')], }, }; ``` ### Css/Sass/Scss Modules [css modules](https://nextjs.org/docs/basic-features/built-in-css-support#adding-component-level-css) work as expected. ```js // This import works just fine in Storybook now 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 ( ); } ``` ### Styled JSX The built in CSS-in-JS solution for Next.js is [styled-jsx](https://nextjs.org/docs/basic-features/built-in-css-support#css-in-js), and this framework supports that out of the box too, zero config. ```js // This works just fine in Storybook now function HelloWorld() { return (scoped!