Configuring Webpack for CSS, Sass/SCSS, and CSS Modules
This guide walks through wiring up Webpack to handle plain CSS, upgrade to Sass/SCSS, and enable CSS Modules for scoped class names.
1) Load plain CSS into JavaScript with style-loader and css-loader
Install loaders:
yarn add -D style-loader css-loader
Minimal rule to inline CSS into the page and resolve @import/url():
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: { sourceMap: true },
},
],
},
],
},
};
Usage in a React component:
/* src/components/DemoA/styles.css */
.container h2::before {
content: '[A]';
}
// src/components/DemoA/index.tsx
import React from 'react';
import './styles.css';
export default function DemoA() {
return (
<section className="container">
<h2>Demo A</h2>
</section>
);
}
2) Move from CSS to Sass/SCSS with sass-loader
Install Sass support. Prefer the modern sass package (Dart Sass):
yarn add -D sass-loader sass
Add a rule for .scss files:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.(scss|sass)$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: { sourceMap: true },
},
{
loader: 'sass-loader',
options: { sourceMap: true },
},
],
},
],
},
};
Example files:
/* src/components/DemoB/styles.scss */
.wrapper {
h2::before {
content: '[B]';
}
}
// src/components/DemoB/index.tsx
import React from 'react';
import './styles.scss';
export default function DemoB() {
return (
<div className="wrapper">
<h2>Demo B</h2>
</div>
);
}
3) Scope class names with CSS Modules
CSS Modules localize selectors to prevent clashes. Enable it through css-loader options. A common pattern is to treat files matching .module.scss (or .module.css) as modules and keep others global.
Recommended setup with separate rules:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
// CSS Modules for *.module.(scss|sass|css)
{
test: /\.module\.(scss|sass|css)$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[path][name]__[local]--[hash:base64:5]',
},
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: { sourceMap: true },
},
],
},
// Global SCSS (not modules)
{
test: /\.(scss|sass)$/i,
exclude: /\.module\.(scss|sass)$/i,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
],
},
};
Module stylesheet and component:
/* src/components/DemoC/styles.module.scss */
.box {
h2::before {
content: '[C]';
}
}
// src/components/DemoC/index.tsx
import React from 'react';
import styles from './styles.module.scss';
export default function DemoC() {
return (
<div className={styles.box}>
<h2>Demo C</h2>
</div>
);
}
TypeScript typings for CSS Modules
TypeScript needs module declarations to import *.module.scss (and similar) as objects. Add a declaration file under src/types/css-modules.d.ts (or any included path):
// src/types/css-modules.d.ts
declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.scss' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.sass' {
const classes: { readonly [key: string]: string };
export default classes;
}
Troubleshooting and tips
- Use
sassinstead ofnode-sass(node-sass is deprecated and often fails to install on some Node versions). Ensure bothsassandsass-loaderare present. - Loader order matters. Webpack applies loaders from right to left:
sass-loader→css-loader→style-loader. - Avoid overlapping rules that process the same files in different ways. If you mix module and global styles, use sepraate
test/excludepatterns as shown. - Slow Sass installation can be network-related. Consider caching, mirrors, or a local registry proxy if installs time out.