From d182f66b4bd30aad6439fe26b16ba1386e513358 Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Tue, 9 Jan 2024 16:31:45 -0700 Subject: [PATCH 1/6] Add Next.js framework doc - Based on - https://github.com/storybookjs/storybook/tree/next/code/frameworks/nextjs - https://storybook.js.org/blog/integrate-nextjs-and-storybook-automatically/ --- docs/get-started/nextjs.md | 935 ++++++++++++++++++ .../react/nextjs-add-framework.js.mdx | 16 + .../react/nextjs-add-framework.ts.mdx | 20 + .../react/nextjs-app-directory-in-meta.js.mdx | 13 + .../nextjs-app-directory-in-meta.ts-4-9.mdx | 16 + .../react/nextjs-app-directory-in-meta.ts.mdx | 16 + .../nextjs-app-directory-in-preview.js.mdx | 13 + .../nextjs-app-directory-in-preview.ts.mdx | 16 + .../react/nextjs-configure-svgr.js.mdx | 25 + .../react/nextjs-configure-svgr.ts.mdx | 29 + .../react/nextjs-image-static-dirs.js.mdx | 12 + .../react/nextjs-image-static-dirs.ts.mdx | 16 + docs/snippets/react/nextjs-install.npm.js.mdx | 3 + .../snippets/react/nextjs-install.pnpm.js.mdx | 3 + .../snippets/react/nextjs-install.yarn.js.mdx | 3 + ...nextjs-navigation-override-in-story.js.mdx | 28 + ...js-navigation-override-in-story.ts-4-9.mdx | 33 + ...nextjs-navigation-override-in-story.ts.mdx | 33 + ...push-override-in-preview-incomplete.js.mdx | 17 + ...push-override-in-preview-incomplete.ts.mdx | 20 + ...navigation-push-override-in-preview.js.mdx | 21 + ...navigation-push-override-in-preview.ts.mdx | 24 + ...ents-for-use-param-override-in-meta.js.mdx | 31 + ...for-use-params-override-in-meta.ts-4-9.mdx | 34 + ...nts-for-use-params-override-in-meta.ts.mdx | 34 + ...avigation-segments-override-in-meta.js.mdx | 28 + ...ation-segments-override-in-meta.ts-4-9.mdx | 31 + ...avigation-segments-override-in-meta.ts.mdx | 31 + .../react/nextjs-next-rsc-in-meta.js.mdx | 11 + .../react/nextjs-next-rsc-in-meta.ts-4-9.mdx | 14 + .../react/nextjs-next-rsc-in-meta.ts.mdx | 14 + docs/snippets/react/nextjs-next-rsc.js.mdx | 9 + docs/snippets/react/nextjs-next-rsc.ts.mdx | 13 + .../react/nextjs-remove-addons.js.mdx | 12 + .../react/nextjs-remove-addons.ts.mdx | 16 + .../nextjs-router-override-in-story.js.mdx | 24 + ...nextjs-router-override-in-story.ts-4-9.mdx | 29 + .../nextjs-router-override-in-story.ts.mdx | 29 + ...push-override-in-preview-incomplete.js.mdx | 17 + ...push-override-in-preview-incomplete.ts.mdx | 20 + ...tjs-router-push-override-in-preview.js.mdx | 21 + ...tjs-router-push-override-in-preview.ts.mdx | 24 + docs/toc.js | 12 + 43 files changed, 1766 insertions(+) create mode 100644 docs/get-started/nextjs.md create mode 100644 docs/snippets/react/nextjs-add-framework.js.mdx create mode 100644 docs/snippets/react/nextjs-add-framework.ts.mdx create mode 100644 docs/snippets/react/nextjs-app-directory-in-meta.js.mdx create mode 100644 docs/snippets/react/nextjs-app-directory-in-meta.ts-4-9.mdx create mode 100644 docs/snippets/react/nextjs-app-directory-in-meta.ts.mdx create mode 100644 docs/snippets/react/nextjs-app-directory-in-preview.js.mdx create mode 100644 docs/snippets/react/nextjs-app-directory-in-preview.ts.mdx create mode 100644 docs/snippets/react/nextjs-configure-svgr.js.mdx create mode 100644 docs/snippets/react/nextjs-configure-svgr.ts.mdx create mode 100644 docs/snippets/react/nextjs-image-static-dirs.js.mdx create mode 100644 docs/snippets/react/nextjs-image-static-dirs.ts.mdx create mode 100644 docs/snippets/react/nextjs-install.npm.js.mdx create mode 100644 docs/snippets/react/nextjs-install.pnpm.js.mdx create mode 100644 docs/snippets/react/nextjs-install.yarn.js.mdx create mode 100644 docs/snippets/react/nextjs-navigation-override-in-story.js.mdx create mode 100644 docs/snippets/react/nextjs-navigation-override-in-story.ts-4-9.mdx create mode 100644 docs/snippets/react/nextjs-navigation-override-in-story.ts.mdx create mode 100644 docs/snippets/react/nextjs-navigation-push-override-in-preview-incomplete.js.mdx create mode 100644 docs/snippets/react/nextjs-navigation-push-override-in-preview-incomplete.ts.mdx create mode 100644 docs/snippets/react/nextjs-navigation-push-override-in-preview.js.mdx create mode 100644 docs/snippets/react/nextjs-navigation-push-override-in-preview.ts.mdx create mode 100644 docs/snippets/react/nextjs-navigation-segments-for-use-param-override-in-meta.js.mdx create mode 100644 docs/snippets/react/nextjs-navigation-segments-for-use-params-override-in-meta.ts-4-9.mdx create mode 100644 docs/snippets/react/nextjs-navigation-segments-for-use-params-override-in-meta.ts.mdx create mode 100644 docs/snippets/react/nextjs-navigation-segments-override-in-meta.js.mdx create mode 100644 docs/snippets/react/nextjs-navigation-segments-override-in-meta.ts-4-9.mdx create mode 100644 docs/snippets/react/nextjs-navigation-segments-override-in-meta.ts.mdx create mode 100644 docs/snippets/react/nextjs-next-rsc-in-meta.js.mdx create mode 100644 docs/snippets/react/nextjs-next-rsc-in-meta.ts-4-9.mdx create mode 100644 docs/snippets/react/nextjs-next-rsc-in-meta.ts.mdx create mode 100644 docs/snippets/react/nextjs-next-rsc.js.mdx create mode 100644 docs/snippets/react/nextjs-next-rsc.ts.mdx create mode 100644 docs/snippets/react/nextjs-remove-addons.js.mdx create mode 100644 docs/snippets/react/nextjs-remove-addons.ts.mdx create mode 100644 docs/snippets/react/nextjs-router-override-in-story.js.mdx create mode 100644 docs/snippets/react/nextjs-router-override-in-story.ts-4-9.mdx create mode 100644 docs/snippets/react/nextjs-router-override-in-story.ts.mdx create mode 100644 docs/snippets/react/nextjs-router-push-override-in-preview-incomplete.js.mdx create mode 100644 docs/snippets/react/nextjs-router-push-override-in-preview-incomplete.ts.mdx create mode 100644 docs/snippets/react/nextjs-router-push-override-in-preview.js.mdx create mode 100644 docs/snippets/react/nextjs-router-push-override-in-preview.ts.mdx diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md new file mode 100644 index 000000000000..1b6b1d060ff7 --- /dev/null +++ b/docs/get-started/nextjs.md @@ -0,0 +1,935 @@ +--- +title: Storybook for Next.js +--- + +Storybook for Next.js is a [framework](../contribute/framework.md) that makes it easy to develop and test UI components in isolation for [Next.js](https://nextjs.org/) applications. It includes: + +- πŸ”€ Routing +- πŸ–Ό Image optimization +- ‡️ Absolute imports +- 🎨 Styling +- πŸŽ› Webpack & Babel config +- πŸ’« and more! + +## Requirements + +- Next.js >= 13.5 +- Storybook >= 7.x + +## 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.](./install.md) + +### 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: + + + + + + + +## 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 +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 +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 +// src/components/MyComponent.js +import localFont from 'next/font/local'; + +const localRubikStorm = localFont({ src: './fonts/RubikStorm-Regular.ttf' }); +``` + +You have to tell Storybook where the `fonts` directory is located, via the [`staticDirs` configuration](../api/main-config-static-dirs.md#with-configuration-objects). 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](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 +- uses: chromaui/action@v1 + 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 +// 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 ctions panel if you have the [Storybook actions addon](../essentials/actions.md). + + + +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.md). The framework will shallowly merge whatever you put here into the router. + + + + + + + + + +These overrides can also be applied to [all stories for a component](../api/parameters.md#meta-parameters) or [all stories in your project](../api/parameters.md#project-parameters). Standard [parameter inheritance](../api/parameters.md#parameter-inheritance) rules apply. + + + +### Default Router + +The default values on the stubbed router are as follows (see [globals](../essentials/toolbars-and-globals.md#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/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. + + + + + + + +Doing this yourself looks something like this (make sure you install the `@storybook/addon-actions` package): + + + + + + + +## 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: + + + + + + + +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.md#configure-story-rendering) file to apply it to all stories. + + + + + + + +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](../writing-stories/parameters.md). The framework will shallowly merge whatever you put here into the router. + + + + + + + + + +These overrides can also be applied to [all stories for a component](../api/parameters.md#meta-parameters) or [all stories in your project](../api/parameters.md#project-parameters). Standard [parameter inheritance](../api/parameters.md#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. + + + + + + + +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. + + + + + + + + + +These overrides can also be applied to [a single story](../api/parameters.md#story-parameters) or [all stories in your project](../api/parameters.md#project-parameters). Standard [parameter inheritance](../api/parameters.md#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 +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. + + + + + + + +Doing this yourself looks something like this (make sure you install the `@storybook/addon-actions` package): + + + + + + + +## 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.md#configure-story-rendering) + +```js +// .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 +// 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(__dirname, 'styles')], + }, +}; +``` + +## Css/Sass/Scss Modules + +[css modules](https://nextjs.org/docs/pages/building-your-application/styling/css-modules) work as expected. + +```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 ( + + ); +} +``` + +## Styled JSX + +The built in CSS-in-JS solution for Next.js is [styled-jsx](https://nextjs.org/docs/pages/building-your-application/styling/css-in-js), and this framework supports that out of the box too, zero config. + +```jsx +// This will work in Storybook +function HelloWorld() { + return ( +
+ Hello world +

scoped!

+ + +
+ ); +} + +export default HelloWorld; +``` + +You can use your own babel config too. This is an example of how you can customize styled-jsx. + +```json +// .babelrc or whatever config file you use +{ + "presets": [ + [ + "next/babel", + { + "styled-jsx": { + "plugins": ["@styled-jsx/plugin-sass"] + } + } + ] + ] +} +``` + +## 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 tailwindcss! (See [Next.js' example](https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss)) + +## 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 +// All good! +import Button from 'components/button'; +// Also good! +import styles from 'styles/HomePage.module.css'; + +export default function HomePage() { + return ( + <> +

Hello World

+ - ); -} -``` - -### 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 ( -
- Hello world -

scoped!

- - -
- ); -} - -export default HelloWorld; -``` - -You can use your own babel config too. This is an example of how you can customize styled-jsx. - -```json -// .babelrc or whatever config file you use -{ - "presets": [ - [ - "next/babel", - { - "styled-jsx": { - "plugins": ["@styled-jsx/plugin-sass"] - } - } - ] - ] -} -``` - -### Postcss - -Next.js lets you [customize postcss config](https://nextjs.org/docs/advanced-features/customizing-postcss-config#default-behavior). Thus this framework will automatically handle your postcss config for you. - -This allows for cool things like zero config tailwindcss! (See [Next.js' example](https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss)) - -### Absolute Imports - -Goodbye `../`! Absolute imports from the root directory work just fine. - -```js -// All good! -import Button from 'components/button'; -// Also good! -import styles from 'styles/HomePage.module.css'; - -export default function HomePage() { - return ( - <> -

Hello World

-