storybook: Can't get Storybook to work with CSS Modules using SCSS

Hi,

I’m having problems getting Storybook to work with my react.js project when I’m using CSS Modules which contain SCSS. Below is my webpack.copy.js file which works fine when I run “npm start”.

const paths = require("../../paths");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { presets, plugins } = require("./webpack.config.babel");

module.exports = {
  mode: "development",
  entry: paths.fromRoot("src/index.js"),
  output: {
    path: paths.fromRoot("build"),
    filename: "[name].[hash].js",
  },
  resolve: {
    extensions: [".js", ".jsx"],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            babelrc: false,
            presets: presets,
            plugins: plugins,
          },
        },
      },
      {
        test: /\.html$/,
        use: [{ loader: "html-loader" }],
      },
      {
        test: /\.svg$/,
        use: ["@svgr/webpack"],
      },
      {
        test: /\.(s?css)$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              importLoaders: 1,
              modules: {
                localIdentName: "[name]__[local]___[hash:base64:5]",
              },
            },
          },
          "sass-loader",
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].[hash].css",
    }),
    new HtmlWebpackPlugin({
      template: paths.fromRoot("src/index.html"),
      filename: "./index.html",
      inject: "body",
    }),
  ],
  devServer: {
    contentBase: paths.fromRoot("build"),
    hot: true,
    open: true,
  },
};

and below is a copy of my current Storybook main.js file:

const path = require("path");

module.exports = {
  addons: [
    "storybook-readme/register",
    "@storybook/addon-a11y/register",
    "@storybook/addon-actions/register",
    "@storybook/addon-backgrounds/register",
    "@storybook/addon-docs/register",
    "@storybook/addon-docs/preset",
    "@storybook/addon-events/register",
    "@storybook/addon-jest/register",
    "@storybook/addon-knobs/register",
    "@storybook/addon-links/register",
    "@storybook/addon-viewport/register",
  ],

  stories: ["../src/**/*.stories.[tj]s"],

  webpackFinal: async (config, { configType }) => {
    // get index of css rule
    const ruleCssIndex = config.module.rules.findIndex(
      (rule) => rule.test.toString() === "/\\.css$/"
    );

    // map over the 'use' array of the css rule and set the 'module' option to true
    config.module.rules[ruleCssIndex].use.map((item) => {
      if (item.loader && item.loader.includes("/css-loader/")) {
        item.options.modules = {
          mode: "local",
          localIdentName:
            configType === "PRODUCTION"
              ? "[local]__[hash:base64:5]"
              : "[name]__[local]__[hash:base64:5]",
        };
      }

      return item;
    });

    config.module.rules.push({
      test: /\.scss$/,
      use: ["style-loader", "css-loader", "sass-loader"],
      include: path.resolve(__dirname, "../"),
    });

    return config;
  },
};

When I run storybook

npm run storybook

The Storybook browser window opens but the component has no css styles applied. I can only assume that the css file is not being created from the scss because the Storybook main.js config is wrong.

Can anyone help please…

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 38
  • Comments: 27 (1 by maintainers)

Commits related to this issue

Most upvoted comments

Hi, I managed to get SCSS modules working by adding the following config to the webpackFinal in .storybook/main.js. This solution uses no add-ons/presets, just needed sass and sass-loader@10(see above - v11 didn’t work) modules installed. My use case is fairly simple (using Component.module.scss type files with my components) but might help others.

webpackFinal: async (config) => {
    // add SCSS support for CSS Modules
    config.module.rules.push({
      test: /\.scss$/,
      use: ['style-loader', 'css-loader?modules&importLoaders', 'sass-loader'],
      include: path.resolve(__dirname, '../'),
    });

    return config;
}

In my react app code I am using styles object like so:

import React from 'react';
import styles from './Component.module.scss';

function Component() {
  return (
    <div className={styles.wrapper}>
     foobar
    </div>
  );
}

export default Component;

I had the same issue. For me, it required a few changes. I hope this is relevant to you 😃

Steps to fix

I didnt have sass installed 🤦 So I ran npm install sass --save

The code used to push scss rules seemed to be causing issues. Definetly wasnt helping. Sorry I don’t have more information for ‘why’ but REMOVE the code below from your main.js

// REMOVE THIS

config.module.rules.push({
  test: /\.scss$/,
  use: ["style-loader", "css-loader", "sass-loader"],
  include: path.resolve(__dirname, "../"),
});

// REMOVE THIS

Use a storybook addon for scss Install the addon by running npm install @storybook/preset-scss --save

Then add the following addon to your list of used addons '@storybook/preset-scss',

Summary

OPs main.js should be updated to look like this

const path = require("path");

module.exports = {
  addons: [
    "storybook-readme/register",
    "@storybook/addon-a11y/register",
    "@storybook/addon-actions/register",
    "@storybook/addon-backgrounds/register",
    "@storybook/addon-docs/register",
    "@storybook/addon-docs/preset",
    "@storybook/addon-events/register",
    "@storybook/addon-jest/register",
    "@storybook/addon-knobs/register",
    "@storybook/addon-links/register",
    "@storybook/addon-viewport/register",
    "@storybook/preset-scss",
  ],

  stories: ["../src/**/*.stories.[tj]s"],

  webpackFinal: async (config, { configType }) => {
    // get index of css rule
    const ruleCssIndex = config.module.rules.findIndex(
      (rule) => rule.test.toString() === "/\\.css$/"
    );

    // map over the 'use' array of the css rule and set the 'module' option to true
    config.module.rules[ruleCssIndex].use.map((item) => {
      if (item.loader && item.loader.includes("/css-loader/")) {
        item.options.modules = {
          mode: "local",
          localIdentName:
            configType === "PRODUCTION"
              ? "[local]__[hash:base64:5]"
              : "[name]__[local]__[hash:base64:5]",
        };
      }

      return item;
    });

    return config;
  },
};

References

https://storybook.js.org/docs/react/api/presets

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

@ericrinaldo thank you! After a long struggle, a combination of your response and these Stack Overflow questions helped:

https://stackoverflow.com/questions/68580273/type-error-this-getoptions-is-not-a-function-for-style-loader/68580324#68580324

https://stackoverflow.com/questions/66082397/typeerror-this-getoptions-is-not-a-function/66087132#66087132

Other than some slight tweaks to dependencies, I got it working with @ericrinaldo’s code. My dependencies:

    "webpack": "^5.61.0"
    "@react-theming/storybook-addon": "^1.1.3",
    "@storybook/addon-essentials": "6.4.0",
    "@storybook/addon-links": "6.4.0",
    "@storybook/react": "^6.4.0-rc.3",
    "css-loader": "5.2.7",
    "style-loader": "2.0.0",
    "sass": "^1.43.4",
    "sass-loader": "10.2.0",

This worked for me:

  • Updated packages
  • No addon or preset
  • Mix between modules and non-module scss files

dependency versions:

"@storybook/react": "^6.4.0-rc.3",
"css-loader": "6.5.1",
"sass": "^1.43.4",
"sass-loader": "^12.3.0",
"style-loader": "2.0.0",
"webpack": "5.31.2",
// .storybook/main.js

const path = require('path');

module.exports = {
  ...
  webpackFinal: async (config) => {
    config.module.rules.push(
      {
        test: /\.s(a|c)ss$/,
        include: path.resolve(__dirname, '../'),
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                auto: true,
                localIdentName: '[name]__[local]--[hash:base64:5]',
              },
            },
          },
          'sass-loader'
        ],
      },
    );
    return config;
  },
  ...
};

// Button.jsx

import React from 'react';
import styles from './Button.module.scss';

export const Button = () => (
  <button className={styles.button}}>
    button
  </button>
);

// Button.module.scss

.button {
  background: green;
}
<!-- storybook's iframe -->

<button class="Button-module__button--b_aG7">
  button
</button>

The @storybook/preset-scss allows override the rule webpack config, so this is my solution

// .storybook/main.js

const config = {
     addons: [
		...
		// nomodule
		{
			name: `@storybook/preset-scss`,
			options: {
				rule: {
					test: /(?<!\.module).s[ca]ss$/,
				}
			},
		},
		// module
		{
			name: `@storybook/preset-scss`,
			options: {
				rule: {
					test: /\.module\.s[ca]ss$/,
				},
				cssLoaderOptions: {
					modules: {
						localIdentName: '[name]__[local]--[hash:base64:5]',
					},
				}
			},
		},
	],
}

Still same in "@storybook/react": "6.1.18 or 6.1.17" and "@storybook/preset-scss": 1.0.3 I installed storybook using npx sb init and also tried @CraftingGamerTom’s way, but doesn’t works. Implemented example https://github.com/qkreltms/lerna-test1/tree/others

@gwithian’s solution is the only that worked for me. Special attention to use sass-loader@10 👍

Above also worked for us (sass-loader @10), I think the issue is that sass-loader >11 minimum webpack version is now 5

https://github.com/webpack-contrib/sass-loader/blob/v11.1.1/CHANGELOG.md#1100-2021-02-05

Still same in "@storybook/react": "6.1.18 or 6.1.17" and "@storybook/preset-scss": 1.0.3 I installed storybook using npx sb init and also tried @CraftingGamerTom’s way, but doesn’t works. Implemented example https://github.com/qkreltms/lerna-test1/tree/others

Hi, I had the same problem but I was able to fix it by changing the sass-loader version from 11 to 10. I have used the @storybook/preset-scss.

https://stackoverflow.com/questions/66082397/typeerror-this-getoptions-is-not-a-function

Worked for me to copy’n paste my webpack configurations for sass modules into storybook configs:

  webpackFinal: async (config) => {
    // add SCSS support for CSS Modules
    config.module.rules.push({
      test: /\.module\.s(a|c)ss$/,
      use: [
        "style-loader",
        {
          loader: "css-loader",
          options: {
            modules: {
              localIdentName: "[local]__[hash:base64:5]",
              exportLocalsConvention: "camelCase",
            },
            sourceMap: true,
          },
        },
        {
          loader: "sass-loader",
          options: {
            sourceMap: true,
          },
        },
      ],
      include: path.resolve(__dirname, "../"),
    });

    return config;
  },

@7iomka, it looks like ‘PostCSS plugin postcss-nested’ requires PostCSS 8 but Storybook builds with PostCSS 7. (see npm ls postcss)

You can force these package dependencies to use PostCSS 8. (Though it might break something else; there might be a reason why the Storybook dependencies use PostCSS 7).

// package.json
...
  {
    "resolutions": {
      "**/postcss": "^8.4.4",
  }
...

then yarn.

@ericrinaldo You saved my day!!! Yours is the only solution that worked for me. Special thanks for listing your dependency versions, they’re the same as mine.

Hi everyone! Seems like there hasn’t been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don’t have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

This worked for me:

  • Updated packages
  • No addon or preset
  • Mix between modules and non-module scss files

dependency versions:

"@storybook/react": "^6.4.0-rc.3",
"css-loader": "6.5.1",
"sass": "^1.43.4",
"sass-loader": "^12.3.0",
"style-loader": "2.0.0",
"webpack": "5.31.2",
// .storybook/main.js

const path = require('path');

module.exports = {
  ...
  webpackFinal: async (config) => {
    config.module.rules.push(
      {
        test: /\.s(a|c)ss$/,
        include: path.resolve(__dirname, '../'),
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                auto: true,
                localIdentName: '[name]__[local]--[hash:base64:5]',
              },
            },
          },
          'sass-loader'
        ],
      },
    );
    return config;
  },
  ...
};
// Button.jsx

import React from 'react';
import styles from './Button.module.scss';

export const Button = () => (
  <button className={styles.button}}>
    button
  </button>
);
// Button.module.scss

.button {
  background: green;
}
<!-- storybook's iframe -->

<button class="Button-module__button--b_aG7">
  button
</button>

This worked for me!

Hi I have tried the various solutions above and but am still having no luck I get THIS error with Storybook/Tailwind/Nextjs:

ModuleBuildError: Module build failed (from ./node_modules/storybook-addon-sass-postcss/node_modules/sass-loader/dist/cjs.js):
SassError: expected "{".
  ╷
2 │       import API from "!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";

Tailwind and my main scss files are appearing no problem, and css modules appear no problem. My current main.js file looks like this (the commented out sections are what seems to cause the problem. It loads fine with them commented out and returns the error above when they are back in)

const path = require('path');

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  staticDirs: ['../public'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    // '@storybook/preset-scss',
    {
      name: 'storybook-addon-sass-postcss',
      options: {
        loadSassAfterPostCSS: true,
        postcssLoaderOptions: {
          implementation: require('postcss'),
        },
        rule: {
          test: /\.(scss|sass)$/i,
        },
      },
    },
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
  webpackFinal: (config) => {
    // config.module.rules.push({
    //   test: /\.scss$/,
    //   include: path.resolve(__dirname, '../'),
    //   use: [
    //     'style-loader',
    //     {
    //       loader: 'css-loader',
    //       options: {
    //         modules: {
    //           auto: true,
    //           localIdentName: '[name]__[local]--[hash:base64:5]',
    //         },
    //       },
    //     },
    //     'sass-loader',
    //   ],
    // });
    config.resolve.alias = {
      ...config.resolve?.alias,
      '@': [path.resolve(__dirname, '../src/'), path.resolve(__dirname, '../')],
    };
    config.resolve.roots = [
      path.resolve(__dirname, '../public'),
      'node_modules',
    ];
    return config;
  },
};

Does anyone have a solution for this? I feel like I have been spending the last 2 days wrestling with Storybook to get scss modules working…

Hi everyone! Seems like there hasn’t been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don’t have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!