Native CSS

This guide shows how to use webpack native CSS handling with experiments.css.

Getting Started

Enable native CSS support in your webpack configuration:

webpack.config.js

export default {
  experiments: {
    css: true,
  },
};

With this option enabled, webpack can process CSS without adding css-loader and mini-css-extract-plugin for the basic flow.

Importing CSS

After enabling the experiment, import .css files directly from JavaScript:

src/index.js

import "./styles.css";

const element = document.createElement("h1");
element.textContent = "Hello native CSS";
document.body.appendChild(element);

src/styles.css

h1 {
  color: #1f6feb;
}

Webpack will process the CSS and include it in the build output.

CSS Modules

Native CSS support also includes CSS Modules. Use the .module.css extension:

src/button.module.css

.button {
  background: #0d6efd;
  color: white;
  border: 0;
  border-radius: 4px;
  padding: 8px 12px;
}

src/index.js

import * as styles from "./button.module.css";

const button = document.createElement("button");
button.className = styles.button;
button.textContent = "Click me";
document.body.appendChild(button);

You can customize CSS Modules behavior using parser and generator options:

webpack.config.js

export default {
  experiments: {
    css: true,
  },
  module: {
    parser: {
      "css/module": {
        namedExports: true,
      },
    },
    generator: {
      "css/module": {
        exportsConvention: "camel-case-only",
        localIdentName: "[uniqueName]-[id]-[local]",
      },
    },
  },
};

Production Build

With experiments.css: true, webpack provides native CSS extraction and content hashing for CSS assets in production builds.

Compared to the classic setup:

  • Traditional approach: css-loader + mini-css-extract-plugin
  • Native approach: experiments.css with built-in extraction behavior

This reduces configuration and keeps the CSS pipeline closer to webpack core features.

Experimental Status & Known Limitations

experiments.css is explicitly experimental, so treat it as opt-in and test carefully before broad rollout.

Known points to keep in mind:

  • APIs and behavior may still evolve before webpack v6 defaults.
  • Some loader-specific options are not part of native CSS behavior (for example, loader-specific filters).
  • If your project relies on advanced loader chains, validate each part before migrating fully.

Migration Guide

If you currently use css-loader, mini-css-extract-plugin, and style-loader, migrate in small steps.

1) Start from a classic setup

webpack.config.js

import MiniCssExtractPlugin from "mini-css-extract-plugin";

export default {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  plugins: [new MiniCssExtractPlugin()],
};

2) Switch to native CSS

webpack.config.js

export default {
  experiments: {
    css: true,
  },
};

3) Migrate css-loader options first

Most CSS Modules-related options should move to native parser/generator config.

webpack.config.js

export default {
  experiments: {
    css: true,
  },
  module: {
    parser: {
      css: {
        import: true,
        url: true,
      },
      "css/module": {
        namedExports: true,
      },
    },
    generator: {
      "css/module": {
        exportsConvention: "camel-case-only",
        localIdentName: "[uniqueName]-[id]-[local]",
      },
    },
  },
};

Notes:

  • import and url are native parser switches for CSS handling.
  • namedExports controls CSS Modules exports behavior.
  • exportsConvention and localIdentName provide class export/name shaping.
  • css-loader filter-style options are not available as direct equivalents; use webpack mechanisms such as IgnorePlugin when needed.

4) Replace mini-css-extract-plugin

When experiments.css is enabled, webpack provides native CSS extraction and content hash handling for CSS output files.

That means you can remove:

  • MiniCssExtractPlugin.loader from module.rules,
  • the new MiniCssExtractPlugin() plugin instance.

5) Replace style-loader with exportType: "style"

If you used style-loader for runtime style injection, use CSS module parser exportType: "style":

webpack.config.js

export default {
  experiments: {
    css: true,
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        type: "css/module",
        parser: {
          exportType: "style",
        },
      },
    ],
  },
};

This mode injects a <style> element from the webpack runtime and covers the typical style-loader use case.

6) Keep imports unchanged

Your JS imports can stay the same:

import "./styles.css";
import * as styles from "./button.module.css";

7) Validate output in development and production

Check that:

  • styles are applied correctly in development,
  • generated CSS files are emitted for production,
  • CSS Modules exports match your existing usage.
Edit this page·

1 Contributor

phoekerson