Skip to content

Repository files navigation

npm node tests coverage discussion size discord-invite

sass-loader

Loads a Sass/SCSS file and compiles it to CSS.

Getting Started

To begin, you'll need to install sass-loader:

npm install sass-loader sass webpack --save-dev

or

yarn add -D sass-loader sass webpack

or

pnpm add -D sass-loader sass webpack

Note

Webpack has built-in CSS support, so no extra loaders are required to process the CSS generated by sass-loader - just enable experiments.css and set the module type to css/auto.

If you prefer the loader-based setup, install style-loader and css-loader via npm i style-loader css-loader and chain them with sass-loader instead.

sass-loader requires you to install either Dart Sass or Sass Embedded on your own (more documentation can be found below).

This allows you to control the versions of all your dependencies and to choose which Sass implementation to use.

Note

We highly recommend using Sass Embedded or Dart Sass.

Use the sass-loader with the built-in CSS support of webpack (the css/auto module type) to let webpack handle the generated CSS - it extracts styles into a separate file and injects them into the document.

Alternatively, you can chain the sass-loader with the css-loader and the style-loader to immediately apply all styles to the DOM, or with the mini-css-extract-plugin to extract it into a separate file.

Then add the loader to your webpack configuration. For example:

app.js

import "./style.scss";

style.scss

$body-color: red;

body {
  color: $body-color;
}

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        // Lets webpack handle the generated CSS using its built-in CSS support,
        // `css/auto` also enables CSS modules for `*.module.scss` files
        type: "css/auto",
        use: [
          // Compiles Sass to CSS
          "sass-loader",
        ],
      },
    ],
  },
  experiments: {
    // Enables the built-in CSS support of webpack
    css: true,
  },
};

Finally run webpack via your preferred method (e.g., via CLI or an npm script).

Note

All examples below use the built-in CSS support of webpack. If you use css-loader and style-loader (or mini-css-extract-plugin) instead, remove the type and experiments options and put them before the sass-loader in the use array:

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
    ],
  },
};

The style option in production mode

For production mode, the style option defaults to compressed unless otherwise specified in sassOptions.

Resolving import, use and forward at-rules

Webpack provides an advanced mechanism to resolve files.

The sass-loader uses Sass's custom importer feature to pass every @use, @import and @forward request to the webpack resolving engine, so your webpack resolve configuration applies to stylesheets, and you can load Sass modules from node_modules:

@use "bootstrap";

How a request is resolved

For @use "theme" inside src/app.scss, the loader asks webpack for the following and takes the first hit:

  1. the partial src/_theme.sass, src/_theme.scss, src/_theme.css
  2. src/theme.sass, src/theme.scss, src/theme.css
  3. theme as written, so aliases and package requests resolve

Directories resolve through their _index/index file. For @import only, the import-only files _theme.import.scss and theme.import.scss are tried before everything else.

Relative requests win over module ones, so @use "theme" behaves like @use "./theme" when both could match. Keeping both _theme.scss and theme.scss in one directory is ambiguous and Sass reports an error for it, so the order within a directory rarely matters.

What your resolve configuration controls

  • alias - applied to every request, and tried before node_modules
  • modules - extra directories to look in, e.g. src
  • byDependency.sass - requests are resolved with dependencyType: "sass", so this targets stylesheets only
  • plugins, symlinks, roots and the rest of the resolver options

webpack.config.js

module.exports = {
  resolve: {
    alias: { "@styles": path.resolve(__dirname, "src/styles") },
    modules: [path.resolve(__dirname, "src"), "node_modules"],
  },
};

style.scss

@use "@styles/theme" as *; // resolved by `resolve.alias`
@use "abstracts" as *; // resolved by `resolve.modules` to `src/abstracts/_index.scss`

Some options are fixed to match Sass's own algorithm and can't be changed through resolve: the extensions are .sass, .scss and .css (so resolve.extensions doesn't apply here), mainFiles prefer _index/index, mainFields prefer sass and style over main, and conditionNames prefer the sass and style export conditions. Your own mainFields and conditionNames are kept after those.

Packages

A package request resolves through the sass and style conditions of its exports field, falling back to the sass, style and main fields. The pkg: URL scheme is supported as well:

@use "pkg:bootstrap";

When webpack can't resolve a request

The importer hands the request back to Sass, which then applies its own resolution - sassOptions.loadPaths, the SASS_PATH environment variable and any custom importer you configured.

Plain CSS files

Sass compiles @import "theme.css" to a plain CSS @import, so the loader leaves it in the output untouched - whatever handles the CSS afterwards (the built-in CSS support of webpack, css-loader, or the browser) decides what happens with it. @use "theme.css" includes the file's content instead, and is resolved like any other request:

@import "theme.css"; // stays `@import "theme.css";` in the output
@use "theme.css"; // inlines the content of the file

The ~ prefix

Using ~ is deprecated and should be removed from your code, but we still support it for historical reasons.

Why can you remove it? The loader will first try to resolve @use as a relative path. If it cannot be resolved, then the loader will try to resolve it inside node_modules.

Prepending module paths with a ~ tells webpack to search through node_modules.

@use "~bootstrap";

It's important to prepend the path with only ~, because ~/ resolves to the home directory.

Webpack needs to distinguish between bootstrap and ~bootstrap because CSS and Sass files have no special syntax for importing relative files.

Writing @use "style.scss" is the same as @use "./style.scss";

Problems with url(...)

Since Sass implementations don't provide url rewriting, all linked assets must be relative to the output.

  • If webpack handles the generated CSS (i.e. the built-in CSS support or the css-loader), all URLs must be relative to the entry-file (e.g. main.scss).
  • If you're just generating CSS without letting webpack handle it, URLs must be relative to your web root.

You might be surprised by this first issue, as it is natural to expect relative references to be resolved against the .sass/.scss file in which they are specified (like in regular .css files).

Thankfully there are two solutions to this problem:

  • Add the missing URL rewriting using the resolve-url-loader. Place it before sass-loader in the loader chain.

  • Library authors usually provide a variable to modify the asset path. bootstrap-sass for example, has an $icon-font-path.

Options

implementation

Type:

type implementation = object | string;

Default: sass

The special implementation option determines which implementation of Sass to use.

By default, the loader resolves the implementation based on your dependencies. Just add the desired implementation to your package.json (sass or sass-embedded package) and install dependencies.

Example where the sass-loader uses the sass (dart-sass) implementation:

package.json

{
  "devDependencies": {
    "sass-loader": "^7.2.0",
    "sass": "^1.22.10"
  }
}

Example where the sass-loader uses the sass-embedded implementation:

package.json

{
  "devDependencies": {
    "sass-loader": "^7.2.0",
    "sass": "^1.22.10"
  },
  "optionalDependencies": {
    "sass-embedded": "^1.70.0"
  }
}

Note

Using optionalDependencies means that sass-loader can fallback to sass when running on an operating system not supported by sass-embedded

Be aware of the order that sass-loader will resolve the implementation:

  1. sass-embedded
  2. sass

You can specify a specific implementation by using the implementation option, which accepts one of the above values.

object

For example, to always use Dart Sass, you'd pass:

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              // Prefer `dart-sass`, even if `sass-embedded` is available
              implementation: require("sass"),
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

string

For example, to use Dart Sass, you'd pass:

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              // Prefer `dart-sass`, even if `sass-embedded` is available
              implementation: require.resolve("sass"),
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

sassOptions

Type:

type sassOptions =
  | import("sass").StringOptionsWithImporter<"async">
  | ((
      content: string | Buffer,
      loaderContext: LoaderContext,
      meta: any,
    ) => import("sass").StringOptionsWithImporter<"async">);

Default: defaults values for Sass implementation

Options for Dart Sass or Sass Embedded implementation.

Note

The charset option is true by default for dart-sass. We strongly discourage setting this to false because webpack doesn't support files other than utf-8.

Note

The syntax option is scss for the scss extension, indented for the sass extension, and css for the css extension.

Note

Options such as data and url are unavailable and will be ignored.

ℹ We strongly discourage changing the sourceMap option because sass-loader sets it automatically when the sourceMap option is true.

Please consult their respective documentation before using them:

object

Use an object for the Sass implementation setup.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              sassOptions: {
                style: "compressed",
                loadPaths: ["absolute/path/a", "absolute/path/b"],
              },
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

function

Allows configuring the Sass implementation with different options based on the loader context.

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              sassOptions: (loaderContext) => {
                // More information about available properties https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === "styles/foo.scss") {
                  return {
                    loadPaths: ["absolute/path/c", "absolute/path/d"],
                  };
                }

                return {
                  loadPaths: ["absolute/path/a", "absolute/path/b"],
                };
              },
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

sourceMap

Type:

type sourceMap = boolean;

Default: depends on the compiler.devtool value

Enables/disables generation of source maps.

By default generation of source maps depends on the devtool option. All values enable source map generation except eval and false.

ℹ If true, the sourceMap option from sassOptions will be ignored.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
              sassOptions: {
                style: "compressed",
              },
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

additionalData

Type:

type additionalData =
  | string
  | ((content: string | Buffer, loaderContext: LoaderContext) => string);

Default: undefined

Prepends Sass/SCSS code before the actual entry file. In this case, the sass-loader will not override the data option but just prepend the entry's content.

This is especially useful when some of your Sass variables depend on the environment:

string

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              additionalData: `$env: ${process.env.NODE_ENV};`,
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

function

Sync
module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              additionalData: (content, loaderContext) => {
                // More information about available properties https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === "styles/foo.scss") {
                  return `$value: 100px;${content}`;
                }

                return `$value: 200px;${content}`;
              },
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};
Async
module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              additionalData: async (content, loaderContext) => {
                // More information about available properties https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === "styles/foo.scss") {
                  return `$value: 100px;${content}`;
                }

                return `$value: 200px;${content}`;
              },
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

webpackImporter

Type:

type webpackImporter = boolean;

Default: true

Enables/disables the default webpack importer.

This can improve performance in some cases, though use it with caution because aliases and @import at-rules starting with ~ will not work. You can pass your own importer to solve this (see Sass importer documentation).

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              webpackImporter: false,
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

warnRuleAsWarning

Type:

type warnRuleAsWarning = boolean;

Default: true

Treats the @warn rule as a webpack warning.

style.scss

$known-prefixes: webkit, moz, ms, o;

@mixin prefix($property, $value, $prefixes) {
  @each $prefix in $prefixes {
    @if not index($known-prefixes, $prefix) {
      @warn "Unknown prefix #{$prefix}.";
    }

    -#{$prefix}-#{$property}: $value;
  }
  #{$property}: $value;
}

.tilt {
  // Oops, we typo'd "webkit" as "wekbit"!
  @include prefix(transform, rotate(15deg), wekbit ms);
}

The presented code will throw a webpack warning instead of logging.

To ignore unnecessary warnings you can use the ignoreWarnings option.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              warnRuleAsWarning: true,
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

api

Type:

type api = "auto" | "modern" | "modern-compiler";

Default: "auto" for sass (dart-sass) and sass-embedded

Allows you to switch between the modern and modern-compiler APIs. You can find more information here. The modern-compiler option enables the modern API with support for Shared Resources.

When "auto" is used, the loader picks "modern-compiler" whenever the implementation exposes initAsyncCompiler (i.e. recent versions of sass and sass-embedded) and falls back to "modern" otherwise. Combined with sass-embedded, this yields the best build performance out of the box.

Note

Using modern-compiler and sass-embedded together significantly improves performance and decreases build time. They are now selected automatically by the default "auto" API.

Note

The legacy Sass JS API is no longer supported. If you were using api: "legacy", please migrate to the modern API. See the Sass JS API docs to learn how to migrate.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              api: "modern-compiler",
              sassOptions: {
                // Your sass options
              },
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

How to enable @debug output

By default, the output of @debug messages is disabled. Add the following to webpack.config.js to enable them:

module.exports = {
  stats: {
    loggingDebug: ["sass-loader"],
  },
  // ...
};

Examples

Extracts CSS into separate files

For production builds, it's recommended to extract the CSS from your bundle to enable parallel loading of CSS/JS resources.

There are five recommended ways to extract a stylesheet from a bundle:

Webpack emits CSS into separate files on its own, so nothing but experiments.css is required.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: ["sass-loader"],
      },
    ],
  },
  output: {
    // Both options are optional
    cssFilename: "[name].css",
    cssChunkFilename: "[id].css",
  },
  experiments: {
    css: true,
  },
};

webpack.config.js

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

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // fallback to style-loader in development
          process.env.NODE_ENV !== "production"
            ? "style-loader"
            : MiniCssExtractPlugin.loader,
          "css-loader",
          "sass-loader",
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
};

webpack.config.js

const path = require("node:path");

module.exports = {
  entry: [path.resolve(__dirname, "./src/scss/app.scss")],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [],
      },
      {
        test: /\.scss$/,
        exclude: /node_modules/,
        type: "asset/resource",
        generator: {
          filename: "bundle.css",
        },
        use: ["sass-loader"],
      },
    ],
  },
};

4. extract-loader (simpler, but specialized on the css-loader's output)

5. file-loader (deprecated--should only be used in webpack v4)

webpack.config.js

const path = require("node:path");

module.exports = {
  entry: [path.resolve(__dirname, "./src/scss/app.scss")],
  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",
        ],
      },
    ],
  },
};

(source: https://stackoverflow.com/a/60029923/2969615)

Source maps

Enables/disables generation of source maps.

To enable CSS source maps, you'll need to pass the sourceMap option to the sass-loader (and to the css-loader too, when you use it).

webpack.config.js

module.exports = {
  devtool: "source-map", // any "source-map"-like devtool is possible
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        type: "css/auto",
        use: [
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  experiments: {
    css: true,
  },
};

If you want to edit the original Sass files inside Chrome, there's a good blog post. Checkout test/sourceMap for a working example.

Contributing

We welcome all contributions! If you're new here, please take a moment to review our contributing guidelines before submitting issues or pull requests.

CONTRIBUTING

License

MIT

About

Compiles Sass to CSS

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3.9k stars

Watchers

51 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages