42

Im trying to compile scss into a separate css file with no luck. As it is now the css gets into the bundle.js together with all js code. How can i separate my css into its own file?

This is how my config looks:

var path = require("path");

module.exports = {
    entry: "./js/main.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        publicPath: "/dist"
    },
    watch:true,
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: "babel-loader",
                    options: { presets: ["es2015"] }
                }
            },
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: "style-loader"
                    },
                    {
                        loader: "css-loader"
                    },
                    {
                        loader: "sass-loader"
                    }
                ]
            }
        ]
    }
};
koloml
  • 525
  • 2
  • 15
fortyfiveknots
  • 423
  • 1
  • 4
  • 5

4 Answers4

68

Other answers gave me just headache as they were not working. I did a lot of googling and I realized you can compile scss into separate css file without using any additional plugins

webpack.config.js

    const path = require('path');

    module.exports = {
        entry: [
            __dirname + '/src/js/app.js',
            __dirname + '/src/scss/app.scss'
        ],
        output: {
            path: path.resolve(__dirname, 'dist'), 
            filename: 'js/app.min.js',
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: [],
                }, {
                    test: /\.scss$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: { outputPath: 'css/', name: '[name].min.css'}
                        },
                        'sass-loader'
                    ]
                }
            ]
        }
    };

package.json

    {
      "name": "...",
      "version": "1.0.0",
      "description": "...",
      "private": true,
      "dependencies": {},
      "devDependencies": {
        "file-loader": "^5.0.2",
        "node-sass": "^4.13.1",
        "sass-loader": "^8.0.2",
        "webpack": "^4.41.5",
        "webpack-cli": "^3.3.10"
      },
      "scripts": {
        "build": "webpack --config webpack.config.js --mode='production'"
      },
      "keywords": [],
      "author": "...",
      "license": "ISC"
    }

Dependencies:

npm install --save-dev webpack webpack-cli file-loader node-sass sass-loader

How to run JS and SCSS compilation

npm run build
moghwan
  • 1,829
  • 2
  • 17
  • 29
Fusion
  • 5,046
  • 5
  • 42
  • 51
  • 7
    Thanks so much for this! I've been spinning my wheels for hours trying to get this sorted; css-loader was giving me all sorts of problems and nothing on the internet helped. Thanks!! – Joel M. May 07 '20 at 20:04
  • 1
    Thanks!!! Finally an answer that actually worked exactly as required. I spent days on this problem! – Bjarne Gerhardt-Pedersen Jul 17 '20 at 08:16
  • wow! thanks a lot mate. I was like searching this for weeks. I was like how webpack doesn't provide this. Even the docs say only one output can be given. Well finally! or i was about to add gulp in addition – Siva-Dev-Wizard Jul 18 '21 at 17:50
  • Thank you so much, worked exactly as desired! I think a lot of dev hours would be saved if this solution was included in the official documentation for sass-loader @ https://www.npmjs.com/package/sass-loader – Joe Coyle Jun 09 '22 at 16:11
  • You should use Dart Sass (`npm install --save-dev sass`) instead of node-sass. – Maytha8 Aug 13 '22 at 08:09
  • Should note that this is for Webpack v4 and lower (will still work in webpack v5) but in webpack v5 the file-loader is built in now via assets https://webpack.js.org/guides/asset-modules/ making this solution even easier.. – Jessy Nov 15 '22 at 15:05
  • Thanks a lot! In a project of mine I had many `style.scss` files in each directory and it only generated a single `style.css` file, so i had to change `[name]` with `[folder]` after reading the doc a bit. thanks again! https://v4.webpack.js.org/loaders/file-loader/#folder – moghwan Nov 24 '22 at 13:47
17

You can use the MiniCssExtractPlugin. This will extract your css into a separate file.

There are a few parts of your webpack.config.js file you'll need to add to, or change.

You'll need to require the plugin at the top of the file:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

A plugins property is also required in the modules object:

plugins: [
  new MiniCssExtractPlugin({
    filename: "[name].css",
    chunkFilename: "[id].css"
  })
]

And you'll need to change your scss rule. Note the test is slightly different to include .scss files (probably best to name you scss files .scss) and the addition of the sass-loader which you'll need to install with npm. The loaders in the 'use' array operate in reverse order, so sass-loaded goes first, converting scss to css, then the css-loader and then extract plugin extracts the css out again to a separate file:

{
    test: /\.s?css$/,
    use: [
      MiniCssExtractPlugin.loader,
      "css-loader",
      "sass-loader"
    ]
}

So I think your config file will change to this:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
var path = require("path");

module.exports = {
    entry: "./js/main.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        publicPath: "/dist"
    },
    watch:true,
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: "babel-loader",
                    options: { presets: ["es2015"] }
                }
            },
            {
                test: /\.s?css$/,
                use: [
                  MiniCssExtractPlugin.loader,
                  "css-loader",
                  "sass-loader"
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
          filename: "[name].css",
          chunkFilename: "[id].css"
        })
    ]
}

Hope that helps.

Bryan
  • 657
  • 3
  • 10
  • Understood. When you have installed the npm module, how do you get it to actually work? I mean, how would you change the webpack-file? – fortyfiveknots May 17 '18 at 15:16
  • 1
    Thanks! it helped me a bit, but it does not create any css file. I have updated the code in my post. – fortyfiveknots May 18 '18 at 07:23
  • @fortyfiveknots hmm, ok. I will look later, but until this evening. Do you get any errors? – Bryan May 19 '18 at 08:46
  • @fortyfiveknots I have update my answer. I never had the loaders quite right. – Bryan May 20 '18 at 07:59
  • 3
    It can be done without mini-css-plugin. please see the answer of my question here https://stackoverflow.com/questions/55035376/webpack-file-loader-with-sass-loader/55045199#55045199 – Ari Mar 07 '19 at 13:43
17

to update @Fusion 's anwser:

from https://github.com/webpack-contrib/file-loader:

DEPRECATED for v5: please consider migrating to asset modules.

the official docs (https://webpack.js.org/guides/asset-modules/) mention that clearly: as of webpack v5, raw-loader, url-loader, file-loader loaders are now depricated and replaced with Asset Modules, and new 4 module types: asset/resource (which replace file-loader), asset/inline, asset/source, asset are introduced.

so, the new way to compile scss to separate css file:


const path = require('path');

module.exports = {
  entry: [
    __dirname + '/src/js/app.js',
    __dirname + '/src/scss/app.scss'
  ],
  output: {
    path: path.resolve(__dirname, 'dist'), 
    filename: 'js/app.min.js',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [],
      }, {
        test: /\.scss$/,
        exclude: /node_modules/,

        type: 'asset/resource',
        generator: {
          filename: 'css/[name].min.css'
        },

        use: [

          {
            loader: 'file-loader',
            options: { outputPath: 'css/', name: '[name].min.css'}
          },

          'sass-loader'
        ]
      }
    ]
  }
};

the big advantage is that Asset Modules is a webpack built-in and you won't even need to install any 3rd module.

refer to:

Update:

you may realise that webpack generates an extra/unwanted/unexpected js file for non-js files (css, scss ...) despite the methode you use file-loader or asset modules, in this case, https://www.npmjs.com/package/webpack-fix-style-only-entries comes handy to solve this limitation.


const path = require('path');

const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries");

module.exports = {
  entry: [
    __dirname + '/src/js/app.js',
    __dirname + '/src/scss/app.scss'
  ],
  output: {
    path: path.resolve(__dirname, 'dist'), 
    filename: 'js/app.min.js',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [],
      }, {
        test: /\.scss$/,
        exclude: /node_modules/,

        type: 'asset/resource',
        generator: {
          filename: 'css/[name].min.css'
        },

        use: [
          'sass-loader'
        ]
      }
    ]
  },
  plugins: [
    new FixStyleOnlyEntriesPlugin({ extensions:['scss'] }),
  ],
};
cizario
  • 3,995
  • 3
  • 13
  • 27
  • Thanks @cizario. Could you please add how we could add the source mapping for scss too? So we can know where each line of css has come from what line in scss and which file? – Mo Baqeri Dec 07 '22 at 01:43
-1

You should use node-sass from npm.

And if you are using webpack for bundling your site you should follow next steps:

npm install node-sass

In package.json sections scripts add following:

scripts: {
   "scss": "node-sass --watch src -o src"
}

First src is location of sass files and -o src is output path where css files will be served.

Predrag Davidovic
  • 1,411
  • 1
  • 17
  • 20