yeln
yeln

Reputation: 765

Next.js: How to change CSS Modules classes output format?

I am new to NextJS, I need to change the CSS class prefix + suffix to this format:

<li class="desktop__menu-item--2feEH"></li>
<li class="desktop__menu-item--2feEH"></li>
<li class="desktop__menu-item--2feEH"></li>
<li class="desktop__menu-item--2feEH"></li>

How to change the component.module.css CSS classes to the format of [path][name]__[local]--[hash:base64:5], can someone explain it to me please?

Do I need a config.js file? I am using Next.js v10.0.9

Upvotes: 5

Views: 3154

Answers (2)

juliomalves
juliomalves

Reputation: 50288

Next.js doesn't yet provide a built-in way to modify the css-loader options.

However, you can still do so by customising the webpack configuration in your next.config.js. You'll need to manually go through each css-loader module loader and add the desired localIdentName.

// next.config.js

module.exports = {
    webpack: (config) => {
        const rules = config.module.rules
            .find((rule) => typeof rule.oneOf === 'object')
            .oneOf.filter((rule) => Array.isArray(rule.use));

        rules.forEach((rule) => {
            rule.use.forEach((moduleLoader) => {
                if (
                    moduleLoader.loader !== undefined &&
                    moduleLoader.loader.includes('css-loader') &&
                    typeof moduleLoader.options.modules === 'object'
                ) {
                    delete moduleLoader.options.modules.getLocalIdent;
                    moduleLoader.options = {
                        ...moduleLoader.options,
                        modules: {
                            ...moduleLoader.options.modules,
                            localIdentName: '[path][name]__[local]--[hash:base64:5]'
                            // You can also add other css-loader options here
                        }
                    };
                }
            });
        });

        return config;
    }
};

Upvotes: 1

vijaykumar Mahendran
vijaykumar Mahendran

Reputation: 37

for Next.js with version >= 12

the accepted answer only works for Next.js with version < 12.

const loaderUtils = require('loader-utils')

const regexEqual = (x, y) => {
  return (
  x instanceof RegExp &&
  y instanceof RegExp &&
  x.source === y.source &&
  x.global === y.global &&
  x.ignoreCase === y.ignoreCase &&
  x.multiline === y.multiline
  )
}

/**
 * Generate context-aware class names when developing locally
 */
const localIdent = (loaderContext, localIdentName, localName, options) => {
  return (
    loaderUtils
      .interpolateName(loaderContext, btoa(unescape(encodeURIComponent(localName))), options)
      // Webpack name interpolation returns `about_about.module__root` for
      // `.root {}` inside a file named `about/about.module.css`. Let's simplify
      // this.
      .replace(/\.module_/, '_')
      // Replace invalid symbols with underscores instead of escaping
      // https://mathiasbynens.be/notes/css-escapes#identifiers-strings
      .replace(/[^a-zA-Z0-9-_]/g, '_')
      // "they cannot start with a digit, two hyphens, or a hyphen followed by a digit [sic]"
      // https://www.w3.org/TR/CSS21/syndata.html#characters
      .replace(/^(\d|--|-\d)/, '__$1')
  )
}

function cssLoaderOptions(modules) {
  const { getLocalIdent, ...others } = modules // Need to delete getLocalIdent else localIdentName doesn't work
  return {
    ...others,
    getLocalIdent: localIdent,
    mode: 'local',
  }
}

const path = require('path')
module.exports = {
  webpack: config => {
    config.resolve.modules.push(path.resolve('./'))
    const oneOf = config.module.rules.find((rule) => typeof rule.oneOf === 'object')
   
    if (oneOf) {
      // Find the module which targets *.scss|*.sass files
      const moduleSassRule = oneOf.oneOf.find(
        (rule) => regexEqual(rule.test, /\.module\.(scss|sass)$/) //highlight-line
      )
      
      if (moduleSassRule) {
        // Get the config object for css-loader plugin
        const cssLoader = moduleSassRule.use.find(
          ({ loader }) => loader.includes('css-loader') //highlight-line
        )
        console.log(cssLoader,"cssloader")
        if (cssLoader) {
          cssLoader.options = {
            ...cssLoader.options,
            modules: cssLoaderOptions(cssLoader.options.modules), //highlight-line
           
          }
        }
      }
    }

  return config

  },

  eslint: {
    // Warning: This allows production builds to successfully complete even if
    // your project has ESLint errors.
    ignoreDuringBuilds: true,
  }
}

Upvotes: 3

Related Questions