SASS Loaders
Add SASS/SCSS support to Webpack — chain sass-loader, css-loader, and style-loader to compile and bundle SASS stylesheets.
SASS Loaders
In the previous episode you learned how to import CSS files into JavaScript. Now we will add SASS/SCSS support so you can write styles with variables, nesting, and mixins — and have Webpack compile them automatically. This episode chains three loaders together to process SASS files.
The Three-Loader Chain
.scss file → sass-loader → css-loader → style-loader → DOM
// 1. sass-loader compiles SCSS to plain CSS
// 2. css-loader resolves @import and url() in the CSS
// 3. style-loader injects the CSS into the page
| Loader | Input | Output |
|---|---|---|
sass-loader | SASS/SCSS code | Plain CSS |
css-loader | Plain CSS | JavaScript module with CSS string |
style-loader | CSS JavaScript module | Injected <style> tag in the DOM |
Step 1: Install the Packages
npm install sass-loader sass --save-dev
| Package | Purpose |
|---|---|
sass-loader | The Webpack loader that passes files through a SASS compiler |
sass | The Dart SASS compiler (the recommended implementation) |
sass (Dart SASS) is the primary implementation. The older node-sass package is deprecated. If you see tutorials using node-sass, use sass instead — it has the same API but is actively maintained.
Step 2: Add the SASS Rule
Add a new rule to webpack.config.js:
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
}
Three loaders, right to left: sass-loader compiles SCSS to CSS, css-loader processes the CSS, and style-loader injects it into the page.
Step 3: Create a SASS File
src/scss/main.scss
// Variables
$bg-dark: #1a1a2e;
$card-bg: #16213e;
$accent: #667eea;
$accent-alt: #764ba2;
$text-light: #eee;
$text-muted: #9ca3af;
$radius: 16px;
// Mixins
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
@mixin gradient-text($from, $to) {
background: linear-gradient(135deg, $from, $to);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
// Base styles
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: $bg-dark;
color: $text-light;
min-height: 100vh;
@include flex-center;
}
// App container
#app {
text-align: center;
padding: 40px;
background: $card-bg;
border-radius: $radius;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
h1 {
font-size: 2.5rem;
margin-bottom: 12px;
@include gradient-text($accent, $accent-alt);
}
p {
color: $text-muted;
font-size: 1.1rem;
}
}
Step 4: Import the SASS File
Update src/index.js to import the SCSS file instead of CSS:
import './scss/main.scss'; // Changed from CSS to SCSS
import { greet, VERSION } from './utils';
import { render } from './dom';
const message = greet('Webpack');
render('#app', `${message}
Version: ${VERSION}
`);
console.log('App loaded successfully!');
Step 5: Build and Test
npm run build
Webpack compiles the SCSS, processes the CSS, and bundles it into the JavaScript output. The styles are applied when the page loads — all from a single import statement.
Using SASS Partials
SASS partials are files prefixed with an underscore that are meant to be imported, not compiled on their own:
src/scss/
├── main.scss (entry — imports partials)
├── _variables.scss (partial — variables only)
├── _mixins.scss (partial — mixins only)
├── _base.scss (partial — resets and base styles)
└── _components.scss (partial — component styles)
src/scss/_variables.scss
$bg-dark: #1a1a2e;
$card-bg: #16213e;
$accent: #667eea;
$accent-alt: #764ba2;
$text-light: #eee;
$text-muted: #9ca3af;
$radius: 16px;
src/scss/main.scss
@use 'variables' as *;
@use 'mixins' as *;
@use 'base';
@use 'components';
The @use rule (modern SASS) imports partials without the underscore or extension. This keeps your styles modular and organized. sass-loader resolves these imports automatically.
sass-loader Options
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
// Inject variables into every SASS file
additionalData: `@use "src/scss/variables" as *;`,
// Choose the SASS implementation
implementation: require('sass'),
sassOptions: {
// Output style
outputStyle: 'compressed',
},
},
},
],
}
| Option | Purpose |
|---|---|
additionalData | Prepends SASS code to every file — useful for global variables |
implementation | Specifies which SASS compiler to use |
sassOptions.outputStyle | Controls CSS output formatting |
Handling Both CSS and SASS
If your project has both .css and .scss files, you need two separate rules:
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
],
}
Each rule's test regex matches different extensions, so Webpack knows which loader chain to apply.
Complete webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
devtool: 'eval-source-map',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: { directory: path.resolve(__dirname, 'dist') },
port: 3000,
open: true,
hot: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: { presets: ['@babel/preset-env'] },
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
],
},
};
The Complete Loader Pipeline
| File Type | Loader Chain | Result |
|---|---|---|
.js | babel-loader | Transpiled JavaScript in the bundle |
.css | style-loader ← css-loader | CSS injected as <style> tag |
.scss | style-loader ← css-loader ← sass-loader | Compiled SASS injected as <style> tag |
Key Takeaways
- SASS support requires three chained loaders:
sass-loader→css-loader→style-loader - Install
sass-loaderandsass(Dart SASS) — avoid the deprecatednode-sass - Loaders execute right to left in the
usearray — SASS compiles first, then CSS processes, then styles inject sass-loaderresolves SASS@useand@importstatements automatically- Use
additionalDatato inject global variables into every SASS file without manual imports - Keep separate rules for
.cssand.scssfiles if your project uses both - For production, swap
style-loaderwithMiniCssExtractPlugin.loaderto extract CSS into a separate file