Next-plugins: Is it possible to use both CSS modules and plain CSS?

Created on 16 Feb 2019  路  13Comments  路  Source: vercel/next-plugins

In Gatsby, we have the 'Component.module.css' processed as CSS module and 'Component.css' processed as plain CSS. Can we have that feature with this plugin? Thanks.

Most helpful comment

I managed to make it work by using the following next.config.js.
Hope that helps.

const withCss = require('@zeit/next-css');
const path = require('path');

const cssModuleRegex = /\.module\.css$/;

module.exports = withCss({
  cssModules: true,
  cssLoaderOptions: {
    getLocalIdent: (loaderContext, _, localName) => {
      const fileName = path.basename(loaderContext.resourcePath);

      if (cssModuleRegex.test(fileName)) {
        const name = fileName.replace(/\.[^/.]+$/, '');
        return `${name}__${localName}`;
      }

      return localName;
    },
  },
});

All 13 comments

Hey @vijayst! @timneutkens and I just had a conversation about this and will hopefully have more information to share soon.

I'll try to remember to ping you in this issue, but maybe drop us a line in a week if you don't hear from us.

Cheers! 馃

I'm also interested in this. I just ran into an instance where I can't import plain CSS from node modules.

Any update on this?

Following. I switched from create-react-app and now all my global styles are not working.

@Timer @timneutkens any information to share?

I managed to make it work by using the following next.config.js.
Hope that helps.

const withCss = require('@zeit/next-css');
const path = require('path');

const cssModuleRegex = /\.module\.css$/;

module.exports = withCss({
  cssModules: true,
  cssLoaderOptions: {
    getLocalIdent: (loaderContext, _, localName) => {
      const fileName = path.basename(loaderContext.resourcePath);

      if (cssModuleRegex.test(fileName)) {
        const name = fileName.replace(/\.[^/.]+$/, '');
        return `${name}__${localName}`;
      }

      return localName;
    },
  },
});

I'm really surprised this still isn't implemented, it seems like it would be a very common issue for anyone using this plugin.
The above solution worked for me, but it's a bit absurd that such basic functionality needs to be patched in by the users.

Global CSS is now supported (opt-in) -- we're working on CSS Modules out-of-the-box!

https://github.com/zeit/next.js/issues/8626

const withCss = require('@zeit/next-css');
const path = require('path');

const cssModuleRegex = /.module.css$/;

module.exports = withCss({
cssModules: true,
cssLoaderOptions: {
getLocalIdent: (loaderContext, _, localName) => {
const fileName = path.basename(loaderContext.resourcePath);

if (cssModuleRegex.test(fileName)) {
const name = fileName.replace(/.[^/.]+$/, '');
return ${name}__${localName};
}

return localName;
},
},
});

The method is working but somehow not very correct - is there another solution now?

const withCss = require('@zeit/next-css');
const path = require('path');

const cssModuleRegex = /.module.css$/;

module.exports = withCss({
cssModules: true,
cssLoaderOptions: {
getLocalIdent: (loaderContext, _, localName) => {
const fileName = path.basename(loaderContext.resourcePath);

if (cssModuleRegex.test(fileName)) {
const name = fileName.replace(/.[^/.]+$/, '');
return ${name}__${localName};
}

return localName;
},
},
});

The method is working but somehow not very correct - is there another solution now?

Looking back at this now - and you're right. This isn't very correct.

The solution offered in #149 seems better.

const withSass = require('@zeit/next-sass');

module.exports = withSass({
    cssModules: true,
    cssLoaderOptions: {
        importLoaders: 2,
        localIdentName: '[local]___[hash:base64:5]',
    },

    webpack: config => {
        config.module.rules.forEach(rule => {
            if (rule.test.toString().includes('.sass')) {
                rule.rules = rule.use.map(useRule => {
                    if (typeof useRule === 'string') {
                        return { loader: useRule };
                    }

                    if (useRule.loader.startsWith('css-loader')) {
                        return {
                            oneOf: [
                                {
                                    test: new RegExp('.module.sass$'),
                                    loader: useRule.loader,
                                    options: useRule.options
                                },
                                {
                                    loader: useRule.loader,
                                    options: {},
                                },
                            ],
                        };
                    }
                    return useRule;
                });
                delete rule.use;
            }
        });
        return config;
    },
});

@vincentaudoire do you have any idea how to use your solution with [hash:base64:5]?

For those who are struggling with errors likeValidationError: Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
or UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'toString' of undefined use the solution below:

(change sass extension to scss if need)

const withSass = require("@zeit/next-sass");
const withCss = require("@zeit/next-css");

module.exports = withCss(
  withSass({
    cssModules: true,
    cssLoaderOptions: {
      importLoaders: 2,
      localIdentName: "[local]___[hash:base64:5]",
    },

    webpack: config => {
      config.module.rules.forEach(rule => {
        if (rule.test.toString().includes(".sass")) {
          rule.rules = rule.use.map(useRule => {
            if (typeof useRule === "string") {
              return {
                loader: useRule
              };
            }

            if (useRule.loader.startsWith("css-loader")) {
              return {
                oneOf: [{
                    test: new RegExp(".module.sass$"),
                    loader: useRule.loader,
                    options: useRule.options,
                  },
                  {
                    loader: useRule.loader,
                    options: {},
                  },
                ],
              };
            }
            return useRule;
          });
          delete rule.use;
        }
      });
      return config;
    },
  }),
);

The latest version of Next.js supports Global CSS and CSS Modules simultaneously (and Sass), zero configuration required.

I'll close this as it's been resolved in main Next.js.

Was this page helpful?
0 / 5 - 0 ratings