To motivate some of the following tips, here's an overview over how Parcel processes CSS files (in that order):

  • @parcel/transformer-postcss: Applies .postcssrc and might generate a CSS modules map
  • @parcel/transformer-css: Registers @import ... and url(...) into Parcel's graph
  • @parcel/packager-css: Concat all CSS assets into a single bundle.
  • @parcel/optimizer-cssnano: Minify the bundle output from @parcel/packager-css.

As you can see, each asset is processed individually by PostCSS and concatenated with the others afterwards.

Parcel reads PostCSS from these files (in that priority): .postcssrc, .postcssrc.json, .postcssrc.js, postcss.config.js.

ΒΆ CSS Modules

There are two ways to enable CSS modules:

  • Either globally in the PostCSS config file (this way you can also configure the underlying postcss-modules plugin).
"modules": true,
"plugins": {
"postcss-modules": {
"generateScopedName": "_[name]__[local]"
  • Or on a per-file basis: by using the file extension .module.css (or .module.scss, etc.).
.main {
color: grey;
import { main } from "./app.module.css";

export function App() {
return <div className={main} />;

ΒΆ Using postcss-import

Some PostCSS plugins (e.g. postcss-custom-properties) potentionally need to access declarations from other @imported CSS assets.

If you do want to run PostCSS on the whole bundle, we recommend you use postcss-import (to inline @imports) and postcss-url (to rewrite url(...) expressions appropriately).

This isn't done by default because it would have a negative effect on caching (Parcel could't reuse unchanged CSS files anymore).

<link rel="stylesheet" type="text/css" href="./app.css" />
<div id="icon"></div>
"plugins": {
"postcss-import": {},
"postcss-url": {},
"postcss-custom-properties": {}
@import "./config/index.css";

html {
background-color: var(--varColor);
.icon {
width: 50px;
height: 50px;
background-image: var(--varIcon);
:root {
--varColor: red;
--varIcon: url("../icon.svg");