How to handle import of SASS when building isomorphic React app?

Viewed 1487

I currently have the current setup for my React app with SSR support:

  • React app
  • Express server that compiles the React app via webpack
  • Using babel-node to run the Express app with ES6 features etc

Everything is working fine, I fire up the Express app and it compiles my React app etc. However - then I started to CSS modules to the React app, and then of course it all broke down, on the server side only of course. It does not know how to handle my .scss files when they are being imported in the React app.

If we forget about the CSS part, everything is working as I want it to. I can run the whole application in "dev-mode", where I have hot reloading etc, babel-node is transpiling my ES6 node code etc.

And I have setup a build script which is compiling the node source to valid ES5, and the React app gets bundled into a single file. All good.

But how should I be able to keep my setup, but with the CSS modules working without Node is complaining it does not understand that code?

My half-way-solution I came up with was when I build everything for production I tell babel to skip my serverRender.js file (which is the one that imports my App.js, uses renderToString etc, and instead compile that specific file with Webpack (using isomorphic-style-loader, css-loader etc), and outputs it in the right folder for my "server" folder/structure. That works, but it just feels wrong. And then if I come back to running in dev mode, I basically have the same issue again, if I dont setup Webpack for that part of the development too...

Any one that can tell me the better way to do this setup?

2 Answers

I had the same issue working on isomorphic React app. Whatever I tried, I either had a problem described in here: SCSS compilation in an isomorphic React app or the error below:

Error: Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type.

I was able to solve the problem by adding isomorphic-style-loader package to my server configs in the webpack. Here is the client configs for the webpack:

    var browserConfig = {
       //usual stuff
       module: {
         rules: [{
         //... ,
         {
            test: /\.scss$/,
            use: [
                {
                    loader: 'style-loader',
                },
                {
                    loader: 'css-loader',
                    options: {
                        modules: true, //turns on the CSS Module mode
                        importLoaders: 1,
                        localIdentName: '[name]__[local]___[hash:base64:5]', //generates CSS class names
                        sourceMap: true
                    }
                },
                {
                    loader: 'sass-loader'
                }
            ]
        }
    ]
}

};

And server configs:

var serverConfig = { 
   //...
   module: {
    rules: [{
     //... ,
    {
            test: /\.scss$/,
            use: [
                    {
                        loader: 'isomorphic-style-loader',
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            importLoaders: 1,
                            localIdentName: '[name]__[local]___[hash:base64:5]',
                            sourceMap: true
                    }
                },
                    {           
                        loader: 'sass-loader'
                }
            ]
    }    
    ]
},
}

With these configs in place, I was able to create styles.scss with simple styles:

$blueColor: #2196F3;

.component {
    background: $blueColor;
}

Import it in the JS file:

import myStyles from './styles.scss';

And use it inside the React component in render():

<div className={ myStyles.component }>

This solution was kindly provided by DigitalKwarts, you can find more details about this solution here: https://blog.digitalkwarts.com/server-side-rendering-with-reactjs-react-router-v4-react-helmet-and-css-modules/

Let me know if it worked for you or of you were able to come up with a better solution.

I read @Galina answer and it's great. I set up a boilerplate using the article he has mentioned: https://stackoverflow.com/a/68600509/6200607

Its config for webpack.config.js:

const path = require('path');
const isDevelopment = true;

module.exports = [
  {
    name: 'client',
    target: 'web',
    entry: './client.jsx',
    output: {
      path: path.join(__dirname, 'static'),
      filename: 'client.js',
      publicPath: '/static/',
    },
    resolve: {
      extensions: ['.js', '.jsx']
    },
    devtool: 'source-map',
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /(node_modules\/)/,
          use: [
            {
              loader: 'babel-loader',
            }
          ]
        },
        {
          test: /\.scss$/,
          use: [
            {
              loader: 'style-loader',
            },
            {
              loader: "css-loader",
              options: {
                modules: {
                  localIdentName: "[name]__[local]___[hash:base64:5]",
                },
                sourceMap: isDevelopment,
              }
            },
            {
              loader: 'sass-loader'
            }
          ]
        }
      ],
    },
  },
  {
    name: 'server',
    target: 'node',
    entry: './server.jsx',
    output: {
      path: path.join(__dirname, 'static'),
      filename: 'server.js',
      libraryTarget: 'commonjs2',
      publicPath: '/static/',
    },
    devtool: 'source-map',
    resolve: {
      extensions: ['.js', '.jsx']
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /(node_modules\/)/,
          use: [
            {
              loader: 'babel-loader',
            }
          ]
        },
        {
          test: /\.scss$/,
          use: [
            {
              loader: 'isomorphic-style-loader',
            },
            {
              loader: "css-loader",
              options: {
                modules: {
                  localIdentName: "[name]__[local]___[hash:base64:5]",
                },
                sourceMap: isDevelopment,
              }
            },
            {
              loader: 'sass-loader'
            }
          ]
        }
      ],
    },
  }
];

Related