Skip to content

Instantly share code, notes, and snippets.

@justincy

justincy/README.md

Last active Feb 24, 2021
Embed
What would you like to do?
Configure Storybook to work with Next.js, TypeScript, and CSS Modules

In addition to the Storybook for React setup, you'll also need to install these packages:

npm i -D @babel/core babel-loader css-loader style-loader
const path = require('path');
module.exports = {
stories: ['../stories/**/*.stories.js', '../stories/**/*.stories.tsx'],
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
presets: [path.resolve(__dirname, './next-preset.js')]
};
const path = require('path');
module.exports = {
webpackFinal: async (baseConfig, options) => {
// Modify or replace config. Mutating the original reference object can cause unexpected bugs.
const { module = {} } = baseConfig;
const newConfig = {
...baseConfig,
module: {
...module,
rules: [...(module.rules || [])]
}
};
// TypeScript with Next.js
newConfig.module.rules.push({
test: /\.(ts|tsx)$/,
include: [
path.resolve(__dirname, '../components'),
path.resolve(__dirname, '../stories')
],
use: [
{
loader: 'babel-loader',
options: {
presets: ['next/babel'],
plugins: ['react-docgen']
}
}
]
});
newConfig.resolve.extensions.push('.ts', '.tsx');
//
// CSS Modules
// Many thanks to https://github.com/storybookjs/storybook/issues/6055#issuecomment-521046352
//
// First we prevent webpack from using Storybook CSS rules to process CSS modules
newConfig.module.rules.find(
rule => rule.test.toString() === '/\\.css$/'
).exclude = /\.module\.css$/;
// Then we tell webpack what to do with CSS modules
newConfig.module.rules.push({
test: /\.module\.css$/,
include: path.resolve(__dirname, '../components'),
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true
}
}
]
});
return newConfig;
}
};
// If you need global CSS, you can import it here and Storybook will automatically include it in all stories.
// You don't need this if you don't have any global CSS.
import '../src/styles.css';
@kris10cabrera

This comment has been minimized.

Copy link

@kris10cabrera kris10cabrera commented Apr 27, 2020

@justincy thanks for this. Where do main.js, next-preset.js, and preview.js go? Inside of .storybook/?

@justincy

This comment has been minimized.

Copy link
Owner Author

@justincy justincy commented Apr 27, 2020

@kris10cabrera

This comment has been minimized.

Copy link

@kris10cabrera kris10cabrera commented Apr 27, 2020

Thanks. Last question, is Typescript required to use storybook with NextJS? @justincy

@justincy

This comment has been minimized.

Copy link
Owner Author

@justincy justincy commented Apr 27, 2020

@justincy

This comment has been minimized.

Copy link
Owner Author

@justincy justincy commented Apr 27, 2020

@kris10cabrera I don't believe there's anything special you need to do in order to use Storybook with Next.js if you're not using Typescript.

@kris10cabrera

This comment has been minimized.

Copy link

@kris10cabrera kris10cabrera commented Apr 27, 2020

Thank you @justincy

@ademilter

This comment has been minimized.

Copy link

@ademilter ademilter commented May 22, 2020

thaaaanks @justincy

postcss-loader

{
  loader: 'postcss-loader',
  options: {
    sourceMap: true,
    config: {
      path: './.storybook/'
    }
  }
}
@mateusvahl

This comment has been minimized.

Copy link

@mateusvahl mateusvahl commented Jun 1, 2020

my hero

@hasantezcan

This comment has been minimized.

Copy link

@hasantezcan hasantezcan commented Jun 8, 2020

Thanks 🎈

@trinwin

This comment has been minimized.

Copy link

@trinwin trinwin commented Jul 5, 2020

Thank you 🎉

I use scss so I replace CSS module part with this ↓


// SCSS preset for Storybook
 newConfig.module.rules.push({
      test: /\.(s*)css$/,
      loaders: ["style-loader", "css-loader", "sass-loader"],
      include: path.resolve(__dirname, "../src/styles/global.scss"),
 });

@marteinn

This comment has been minimized.

Copy link

@marteinn marteinn commented Aug 4, 2020

Brilliant, thanks for the guide @justincy!

@RichardBosworth

This comment has been minimized.

Copy link

@RichardBosworth RichardBosworth commented Sep 18, 2020

Is there a way to handle SCSS modules?

I've created a "componentname.module.scss" file, and it works in NextJS but can't seem to get it to load in Storybook!

@justincy

This comment has been minimized.

Copy link
Owner Author

@justincy justincy commented Sep 18, 2020

@RichardBosworth See the comment by trinwin about SCSS.

@RichardBosworth

This comment has been minimized.

Copy link

@RichardBosworth RichardBosworth commented Sep 18, 2020

@RichardBosworth See the comment by trinwin about SCSS.

Am I right in thinking that the above only applies to the global SCSS file? I'm trying to get Storybook specifically to work with CSS Modules but using SCSS instead of plain CSS. So my Component file references an "import styles from './componentfile.module.scss'.

Everything works great when I use just plain CSS Modules. But when I try to use sass-loader to load the modules, it throws an error. If I try to pass the options in your original posting to sass-loader, it says that they're incorrect.

@justincy

This comment has been minimized.

Copy link
Owner Author

@justincy justincy commented Sep 18, 2020

@RichardBosworth

Am I right in thinking that the above only applies to the global SCSS file?

I don't believe so. The code snippet appears to be loading both scss and css module files and then explicitly including one global css file.

Did you try it?

@RichardBosworth

This comment has been minimized.

Copy link

@RichardBosworth RichardBosworth commented Sep 22, 2020

@RichardBosworth

Am I right in thinking that the above only applies to the global SCSS file?

I don't believe so. The code snippet appears to be loading both scss and css module files and then explicitly including one global css file.

Did you try it?

So the solution was:

newConfig.module.rules.push({
            test: /\.module\.(s*)css$/,
            include: path.resolve(__dirname, '../components'),
            use: [
                'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 1,
                        modules: true,
                    },
                },
                'sass-loader',
            ],
        });

Adding the sass-loader after the css-loader modules bit. Also adding the (s*)css bit.

@rohanvachheta

This comment has been minimized.

Copy link

@rohanvachheta rohanvachheta commented Oct 2, 2020

thanks

@emadabdulrahim

This comment has been minimized.

Copy link

@emadabdulrahim emadabdulrahim commented Oct 6, 2020

Thanks @justincy!

@oguzkaracar

This comment has been minimized.

Copy link

@oguzkaracar oguzkaracar commented Oct 17, 2020

thaaaanks @justincy

postcss-loader

{
  loader: 'postcss-loader',
  options: {
    sourceMap: true,
    config: {
      path: './.storybook/'
    }
  }
}

addition of this for: postcss-loader@4.0.4

{
   loader: "postcss-loader",
	options: {
	   postcssOptions: {
		sourceMap: true,
		config: path.resolve(__dirname, "../postcss.config.js"),
           },
        },
},
@drskullster

This comment has been minimized.

Copy link

@drskullster drskullster commented Oct 23, 2020

I managed to get SCSS modules working with @storybook/preset-scss :

{
  name: '@storybook/preset-scss',
  options: {
    cssLoaderOptions: {
      modules: {
        auto: true
      }
    }
  }
},

Global style import also works in preview.js.

https://github.com/storybookjs/presets/tree/master/packages/preset-scss

EDIT: I added the auto option to css modules to prevent classes in my global files to be scoped.

@ilkeryilmaz

This comment has been minimized.

Copy link

@ilkeryilmaz ilkeryilmaz commented Oct 23, 2020

I managed to get SCSS modules working with @storybook/preset-scss :

{
  name: '@storybook/preset-scss',
  options: {
    cssLoaderOptions: {
      modules: true,
    }
  }
},

Global style import also works in preview.js.

https://github.com/storybookjs/presets/tree/master/packages/preset-scss

The clean solution, thanks @drskullster

@andymeek

This comment has been minimized.

Copy link

@andymeek andymeek commented Dec 2, 2020

Thanks @justincy!

I'm experiencing some issues with Storybook not applying directives (e.g., @apply) correctly, however, it works with NextJS.

Do you have any ideas about this, and do they work for you?

@justincy

This comment has been minimized.

Copy link
Owner Author

@justincy justincy commented Dec 2, 2020

@andymeek I don't use @apply. Are you using Tailwind? I got that working once with @apply by including postcss-loader.

use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 1,
            modules: true
          }
        }, 'postcss-loader'
      ]
@njcaballero

This comment has been minimized.

Copy link

@njcaballero njcaballero commented Dec 10, 2020

I managed to get SCSS modules working with @storybook/preset-scss :

{
  name: '@storybook/preset-scss',
  options: {
    cssLoaderOptions: {
      modules: {
        auto: true
      }
    }
  }
},

Global style import also works in preview.js.

https://github.com/storybookjs/presets/tree/master/packages/preset-scss

EDIT: I added the auto option to css modules to prevent classes in my global files to be scoped.

This worked for me too! thanks @drskullster

@EyalPerry

This comment has been minimized.

Copy link

@EyalPerry EyalPerry commented Dec 27, 2020

Below is the config I used to get css-modules to work with tailwind + scss + anything you put in postcss- the same as in the SPA via Next.js v10.
Note that I manually installed all of the below packages since I've had some PostCss warnings which indicated I am not passing in any config, which made me suspect PostCss7 was used somewhere below. If you experience the lack of tailwind styling in storybook- that's probably it.
Also note that sass-loader comes last in the loader array.

 const removeIndex = newConfig.module.rules.findIndex(
      (rule) => rule.test.toString() === "/\\.css$/"
    );

    if (removeIndex !== -1) {
      newConfig.module.rules.splice(removeIndex, 1);
    }

    newConfig.module.rules.push({
      test: /\.(s*)css$/,
      loaders: [
        "style-loader",
        {
          loader: "css-loader",
          options: {
            importLoaders: 1,
            modules: { auto: true },
          },
        },
        {
          loader: "postcss-loader",
          options: {
            postcssOptions: {
              config: path.resolve(__dirname, "..", "postcss.config.js"),
            },
          },
        },
        "sass-loader",
      ],
    });

This is my package.json:

"devDependencies": {
    "@storybook/react": "6.1.11",
    "css-loader": "5.0.1",
    "postcss": "8.2.1",
    "postcss-flexbugs-fixes": "5.0.2",
    "postcss-import": "14.0.0",
    "postcss-loader": "4.1.0",
    "postcss-preset-env": "6.7.0",
    "sass": "1.30.0",
    "sass-loader": "10.1.0",
    "style-loader": "2.0.0"
  }

As a result, I was able to use both tailwind and scss syntax in my css modules:

.disabled {
  @apply hover:shadow-none;

  @extend .non-interactive;
}

Make sure you import your global css files in preview.js, the same way your _app component would.

@canerorenn

This comment has been minimized.

Copy link

@canerorenn canerorenn commented Jan 30, 2021

Thanks @justincy

A config for NextJS Absolute Imports and Module path aliases

somewhere convenient in next-preset.js

baseConfig.resolve.alias = {
  ...baseConfig.resolve.alias,
  "@/components": path.resolve(__dirname, "../components"),
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment