reactjsgulppostcsscss-modules

Is it possible to set up a gulp-based build to compile css modules for react components


I have an existing Express project where I'd like to add css modules locally scoped to my react components. The project relies on a gulp build script. I have recently added react to the project to begin adding some individual components and am trying to find a way to use css modules with the current gulp build script.

Here are the relevant lines from my gulp build:

const postcss = require('gulp-postcss');
const postcssModules = require('postcss-modules');

function cssModules() {
  return gulp.src('bca-react-component-library/**/*.css')
    .pipe(postcss([postcssModules()]))
    .pipe(gulp.dest('public/js/dist/bca-react-component-library'));
}

When the browser tries to load the css modules, it returns the following error in the console:

Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/css". Strict MIME type checking is enforced for module scripts per HTML spec.

I'm using gulp-post-css along with the postcss-modules plugin

I understand the basic concept that the browser is trying to import a javascript module but is instead ends up trying to import a css module, resulting in the error, but I think I have a gap in understanding in what postcss should be outputting and how my reach component should be handling it. Per the postcss docs:

By default, a JSON file with exported classes will be placed next to corresponding CSS

However, I am not seeing the json output at this location. I do see the module.css output at this location with the modified class name.

What might I be overlooking?


Solution

  • The issue is that postcss-modules needs to be configured to actually generate the JSON file that maps the original class names to the hashed ones. By default, it processes the CSS but doesn't automatically write the JSON output.

    Here's what you need to do:

    1. Configure postcss-modules to generate JSON

    const postcss = require('gulp-postcss');
    const postcssModules = require('postcss-modules');
    const fs = require('fs');
    const path = require('path');
    
    function cssModules() {
      return gulp.src('bca-react-component-library/**/*.css')
        .pipe(postcss([
          postcssModules({
            getJSON: function(cssFileName, json, outputFileName) {
              const jsonFileName = path.resolve(cssFileName.replace('.css', '.css.json'));
              fs.writeFileSync(jsonFileName, JSON.stringify(json));
            }
          })
        ]))
        .pipe(gulp.dest('public/js/dist/bca-react-component-library'));
    }
    

    2. Use the generated JSON in your React components

    However, here's the catch: React components can't directly import the JSON files at runtime in the browser. You need to transform your CSS imports into JavaScript modules that export the class name mappings.

    Better Solution: Generate JS modules instead

    Modify your gulp task to generate .css.js files:

    function cssModules() {
      return gulp.src('bca-react-component-library/**/*.css')
        .pipe(postcss([
          postcssModules({
            getJSON: function(cssFileName, json, outputFileName) {
              // Generate a .js file that exports the class mappings
              const jsFileName = path.resolve(
                outputFileName || cssFileName
              ).replace('.css', '.css.js');
              
              const jsContent = `export default ${JSON.stringify(json, null, 2)};`;
              fs.writeFileSync(jsFileName, jsContent);
            }
          })
        ]))
        .pipe(gulp.dest('public/js/dist/bca-react-component-library'));
    }
    

    3. Import in your React components

    import styles from './MyComponent.css.js';
    
    function MyComponent() {
      return <div className={styles.myClass}>Hello</div>;
    }