Skip to content

Commit 817551f

Browse files
authored
docs(guides): add native CSS guide for experiments.css (#8193)
1 parent 4859143 commit 817551f

1 file changed

Lines changed: 271 additions & 0 deletions

File tree

src/content/guides/native-css.mdx

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
---
2+
title: Native CSS
3+
sort: 27
4+
contributors:
5+
- phoekerson
6+
---
7+
8+
This guide shows how to use webpack native CSS handling with `experiments.css`.
9+
10+
T> `experiments.css` is still experimental. It is expected to become the default in webpack v6, but behavior can still change while development continues.
11+
12+
## Getting Started
13+
14+
Enable native CSS support in your webpack configuration:
15+
16+
**webpack.config.js**
17+
18+
```js
19+
export default {
20+
experiments: {
21+
css: true,
22+
},
23+
};
24+
```
25+
26+
With this option enabled, webpack can process CSS without adding `css-loader` and `mini-css-extract-plugin` for the basic flow.
27+
28+
## Importing CSS
29+
30+
After enabling the experiment, import `.css` files directly from JavaScript:
31+
32+
**src/index.js**
33+
34+
```js
35+
import "./styles.css";
36+
37+
const element = document.createElement("h1");
38+
element.textContent = "Hello native CSS";
39+
document.body.appendChild(element);
40+
```
41+
42+
**src/styles.css**
43+
44+
```css
45+
h1 {
46+
color: #1f6feb;
47+
}
48+
```
49+
50+
Webpack will process the CSS and include it in the build output.
51+
52+
## CSS Modules
53+
54+
Native CSS support also includes CSS Modules. The recommended approach is:
55+
56+
- keep `type: "css/auto"` for mixed CSS handling,
57+
- use `.module.css` (or `.modules.css`) naming for CSS Modules files.
58+
59+
**src/button.module.css**
60+
61+
```css
62+
.button {
63+
background: #0d6efd;
64+
color: white;
65+
border: 0;
66+
border-radius: 4px;
67+
padding: 8px 12px;
68+
}
69+
```
70+
71+
**src/index.js**
72+
73+
```js
74+
import * as styles from "./button.module.css";
75+
76+
const button = document.createElement("button");
77+
button.className = styles.button;
78+
button.textContent = "Click me";
79+
document.body.appendChild(button);
80+
```
81+
82+
T> CSS Modules class names are exported. By default, named exports are enabled for CSS modules.
83+
84+
You can customize CSS Modules behavior using parser and generator options:
85+
86+
**webpack.config.js**
87+
88+
```js
89+
export default {
90+
experiments: {
91+
css: true,
92+
},
93+
module: {
94+
parser: {
95+
"css/module": {
96+
namedExports: true,
97+
},
98+
},
99+
generator: {
100+
"css/module": {
101+
exportsConvention: "camel-case-only",
102+
localIdentName: "[uniqueName]-[id]-[local]",
103+
},
104+
},
105+
},
106+
};
107+
```
108+
109+
## Production Build
110+
111+
With `experiments.css: true`, webpack provides native CSS extraction and content hashing for CSS assets in production builds.
112+
113+
Compared to the classic setup:
114+
115+
- Traditional approach: `css-loader` + `mini-css-extract-plugin`
116+
- Native approach: `experiments.css` with built-in extraction behavior
117+
118+
This reduces configuration and keeps the CSS pipeline closer to webpack core features.
119+
120+
## Experimental Status & Known Limitations
121+
122+
`experiments.css` is explicitly experimental, so treat it as opt-in and test carefully before broad rollout.
123+
124+
Known points to keep in mind:
125+
126+
- APIs and behavior may still evolve before webpack v6 defaults.
127+
- Some loader-specific options are not part of native CSS behavior (for example, loader-specific filters).
128+
- If your project relies on advanced loader chains, validate each part before migrating fully.
129+
130+
## Migration Guide
131+
132+
If you currently use `css-loader`, `mini-css-extract-plugin`, and `style-loader`, migrate in small steps.
133+
134+
### 1) Start from a classic setup
135+
136+
**webpack.config.js**
137+
138+
```js
139+
import MiniCssExtractPlugin from "mini-css-extract-plugin";
140+
141+
export default {
142+
module: {
143+
rules: [
144+
{
145+
test: /\.css$/i,
146+
use: [MiniCssExtractPlugin.loader, "css-loader"],
147+
},
148+
],
149+
},
150+
plugins: [new MiniCssExtractPlugin()],
151+
};
152+
```
153+
154+
### 2) Switch to native CSS
155+
156+
**webpack.config.js**
157+
158+
```js
159+
export default {
160+
experiments: {
161+
css: true,
162+
},
163+
};
164+
```
165+
166+
### 3) Migrate `css-loader` options first
167+
168+
Most CSS Modules-related options should move to native parser/generator config.
169+
170+
**webpack.config.js**
171+
172+
```js
173+
export default {
174+
experiments: {
175+
css: true,
176+
},
177+
module: {
178+
parser: {
179+
css: {
180+
import: true,
181+
url: true,
182+
},
183+
"css/module": {
184+
namedExports: true,
185+
},
186+
},
187+
generator: {
188+
"css/module": {
189+
exportsConvention: "camel-case-only",
190+
localIdentName: "[local]-[hash:base64:6]",
191+
},
192+
},
193+
},
194+
};
195+
```
196+
197+
Notes:
198+
199+
- `import` and `url` are native parser switches for CSS handling.
200+
- `namedExports` controls CSS Modules exports behavior.
201+
- `exportsConvention` and `localIdentName` provide class export/name shaping.
202+
- `localIdentName` supports hash placeholders (for example `[hash:base64:6]`); you can tune hashing globally with [`output.hashFunction`](/configuration/output/#outputhashfunction), [`output.hashDigest`](/configuration/output/#outputhashdigest), [`output.hashDigestLength`](/configuration/output/#outputhashdigestlength), and [`output.hashSalt`](/configuration/output/#outputhashsalt).
203+
- `css-loader` filter-style options are not available as direct equivalents; use webpack mechanisms such as `IgnorePlugin` when needed.
204+
205+
### 4) Replace `mini-css-extract-plugin`
206+
207+
When `experiments.css` is enabled, webpack provides native CSS extraction and content hash handling for CSS output files, so you can drop both `mini-css-extract-plugin` and `css-loader` from this basic setup.
208+
209+
**webpack.config.js**
210+
211+
```diff
212+
-import MiniCssExtractPlugin from "mini-css-extract-plugin";
213+
-
214+
export default {
215+
+ experiments: {
216+
+ css: true,
217+
+ },
218+
- plugins: [new MiniCssExtractPlugin()],
219+
};
220+
```
221+
222+
You can remove:
223+
224+
- the CSS loader chain (including `css-loader`) from `module.rules`,
225+
- the `new MiniCssExtractPlugin()` plugin instance.
226+
227+
### 5) Replace `style-loader` with `exportType: "style"`
228+
229+
If you used `style-loader` for runtime style injection, keep `css/auto` and use module naming (`.module.css` or `.modules.css`), then set `exportType: "style"`:
230+
231+
**webpack.config.js**
232+
233+
```js
234+
export default {
235+
experiments: {
236+
css: true,
237+
},
238+
module: {
239+
rules: [
240+
{
241+
test: /\.css$/i,
242+
type: "css/auto",
243+
parser: {
244+
exportType: "style",
245+
},
246+
},
247+
],
248+
},
249+
};
250+
```
251+
252+
This mode injects a `<style>` element from the webpack runtime and covers the typical `style-loader` use case.
253+
254+
If you cannot rename files to the CSS Modules naming convention, you can use `type: "css/module"` directly for the relevant rule.
255+
256+
### 6) Keep imports unchanged
257+
258+
Your JS imports can stay the same:
259+
260+
```js
261+
import "./styles.css";
262+
import * as styles from "./button.module.css";
263+
```
264+
265+
### 7) Validate output in development and production
266+
267+
Check that:
268+
269+
- styles are applied correctly in development,
270+
- generated CSS files are emitted for production,
271+
- CSS Modules exports match your existing usage.

0 commit comments

Comments
 (0)