What is PostCSS?
PostCSS is a tool that transforms CSS with JavaScript plugins. On its own it does almost nothing. It parses your CSS into a tree, hands that tree to a list of plugins, and serialises the result back into CSS. The plugins are where all the behaviour lives.
That design is why PostCSS is everywhere. Autoprefixer, the plugin that adds vendor prefixes, is a PostCSS plugin. Tailwind CSS is a PostCSS plugin. cssnano, the minifier, is a PostCSS plugin. You are probably using PostCSS already without having installed it directly.
How it works
The pipeline has three stages.
- Parse. PostCSS reads a stylesheet and builds an abstract syntax tree (AST) — a structured representation of every rule, declaration, at-rule and comment.
- Transform. Each plugin walks the tree and mutates it. One plugin might add prefixes, another might inline imports, another might minify.
- Stringify. PostCSS writes the modified tree back out as CSS.
Because plugins operate on a tree rather than on text, they can make precise, safe changes. A plugin can insert a declaration, rename a selector or drop a rule entirely without brittle string replacement.
Configuration
PostCSS reads its plugin list from a config file in your project root. The modern format uses an object keyed by plugin name, and the order of keys is the order they run.
// postcss.config.js
export default {
plugins: {
"postcss-import": {},
"postcss-preset-env": { stage: 3 },
autoprefixer: {},
cssnano: process.env.NODE_ENV === "production" ? {} : false,
},
};
Setting a plugin to false disables it for that environment, which is how you keep minification out of development builds.
Autoprefixer
Autoprefixer is the plugin that made PostCSS famous. You write standard CSS and it adds the vendor prefixes your target browsers need, based on your browserslist configuration.
/* input.css */
.card {
display: flex;
user-select: none;
}
With sensible targets, Autoprefixer emits the -webkit- and -moz- variants automatically. As browser support changes, updating browserslist is enough — you never hand-edit prefixes again.
// package.json
{
"browserslist": ["> 0.5%", "last 2 versions", "not dead"]
}
postcss-preset-env
postcss-preset-env bundles a curated set of plugins that let you write future CSS today. It covers nesting, custom media queries, :is() and :where(), logical properties and more, translating them based on your browser targets.
/* modern.css */
.card {
color: oklch(70% 0.1 200);
&:hover {
color: oklch(75% 0.1 200);
}
}
The stage option controls how experimental the accepted features are, from stage: 0 (very new) to stage: 4 (stable). A common choice is stage: 3.
Other useful plugins
- postcss-import inlines
@importstatements into a single file, which reduces requests and lets bundlers see the whole graph. - postcss-nested adds Sass-like nesting for projects that cannot rely on native nesting yet.
- cssnano minifies the output — it removes whitespace, merges rules, shortens colours and drops duplicates.
- postcss-custom-media and postcss-custom-properties polyfill custom media and custom properties where needed.
- stylelint shares the same AST and is often run alongside PostCSS for linting.
Integration with build tools
You rarely invoke PostCSS directly. Build tools run it for you.
- Vite processes CSS through PostCSS automatically and picks up
postcss.config.js. - Next.js ships PostCSS support and uses it for Tailwind and Autoprefixer.
- Angular runs PostCSS internally for its component styles.
- webpack uses
postcss-loaderin the CSS rule chain.
The practical upshot: add a config file and your existing imports get transformed with no other changes.
Writing a plugin
A plugin is a function that returns an object with a postcssPlugin name and a Once hook. The hook receives the root node and can walk it however it likes.
// postcss-uppercase-hex.js
export default function uppercaseHex() {
return {
postcssPlugin: "uppercase-hex",
Declaration(decl) {
decl.value = decl.value.replace(/#[0-9a-f]{3,8}/gi, (hex) => hex.toUpperCase());
},
};
}
uppercaseHex.postcss = true;
Register it in the config by importing the function instead of naming it as a string. Small plugins like this are a clean way to enforce project conventions across every stylesheet.
Plugin order
Plugins run in sequence, and each one sees the output of the last. A sensible order is:
- Imports first, so later plugins see one combined file.
- Syntax transforms such as
postcss-preset-envand nesting next. - Autoprefixer after the syntax is final, so prefixes match the actual output.
- Minification last, once nothing else will change.
Reversing the order can produce prefixes on syntax that later disappears, or a minified file that a later plugin then reformats.
When to use PostCSS
Use PostCSS when you want to write modern CSS and ship something your users’ browsers understand, when you need vendor prefixes without the maintenance, or when you want to optimise production CSS. If you already use Vite or Next.js, you are likely one config file away.
You may not need to think about it much if you use a framework that bundles it. But understanding the pipeline explains why your Tailwind classes appear, where the prefixes come from and how to add a transformation of your own.
Best practices
- Define browserslist explicitly; it drives Autoprefixer and preset-env.
- Keep the plugin list short and ordered intentionally.
- Use
postcss-importto combine files before other transformations. - Only enable cssnano in production.
- Pin plugin versions and review upgrades, since transformations affect output.
- Prefer native CSS features over plugins when browser support allows.
- Write a small custom plugin instead of a fragile global find-and-replace.
Common mistakes
- Expecting PostCSS to do something without any plugins configured.
- Getting the plugin order wrong and losing prefixes or minification.
- Leaving browserslist undefined, so plugins fall back to very broad defaults.
- Running cssnano in development and making the CSS unreadable.
- Treating PostCSS as a Sass replacement when the two solve different problems.
- Adding many overlapping plugins and producing bloated, unpredictable output.
Where to go next
PostCSS is plumbing, but it powers the tools you use every day. See how it drives Tailwind CSS, how it complements Sass, and how Vite runs it automatically. With the CSS fundamentals in hand, you can shape the pipeline to fit any project.