Ship modern CSS without manual vendor prefixes using PostCSS
Use PostCSS with postcss-preset-env and Autoprefixer to write modern CSS and ship browser-compatible output at build time.
21 Aug 2026, 02:23 UTC

Your CSS works locally, but breaks in older browsers
You write clean, modern CSS with nesting and custom media queries. Then a colleague opens the site in an older browser and the layout collapses. The usual fix is hunting through stylesheets to add vendor prefixes like -webkit- or -moz-, or simply avoiding newer syntax altogether. Both approaches slow down development and leave gaps in coverage.
The practical alternative is to treat browser compatibility as a build-step problem instead of a hand-written one. PostCSS parses your CSS into an abstract syntax tree—a structured representation of rules, selectors, and declarations—and runs JavaScript plugins to transform it before the file reaches the browser. Because this happens at build time, there is no runtime cost to users.
Use postcss-preset-env for syntax, Autoprefixer for prefixes
postcss-preset-env lets you write future CSS features such as nesting and custom media queries. It converts them into widely supported CSS based on your target browsers. Autoprefixer then adds vendor prefixes only where your target audience still needs them, using Can I Use data. The two plugins solve different parts of the same problem: preset-env handles syntax transformation, while Autoprefixer handles prefix injection.
Plugin order matters. Run syntax transforms before Autoprefixer so prefixes are applied to the final transformed properties rather than the original modern ones.
Worked example: configure and compile
Install the tools in your project:
npm install --save-dev postcss postcss-cli autoprefixer postcss-preset-env
Create postcss.config.js in the project root:
module.exports = {
plugins: [
require('postcss-preset-env')({
stage: 0,
features: {
'nesting-rules': true
}
}),
require('autoprefixer')
]
};
Add a browserslist query to package.json so both plugins know which browsers to target:
"browserslist": [
"last 2 versions",
"not dead",
">= 2%"
]
Run the compiler from your terminal in the project root. No special permissions are required, but Node.js and the installed dependencies must be present:
npx postcss --use autoprefixer --use postcss-preset-env src/style.css -o dist/style.css
Replace src/style.css and dist/style.css with your actual input and output paths. The command overwrites dist/style.css if it exists, so check that path before running. Afterward, inspect dist/style.css to confirm that nested rules have been flattened and that vendor prefixes appear only for the browsers in your query.
To verify your target list, run:
npx browserslist
This prints the resolved browser versions. If the list looks wrong—for example, it includes a browser your analytics show you no longer support—update the browserslist query and rebuild.
Trade-offs and limitations
- Build dependency: Your pipeline now depends on Node.js and PostCSS plugins. A broken install blocks CSS builds.
- Not a full preprocessor: PostCSS is not Sass. Features such as variables and mixins need explicit plugins and do not work out of the box.
- Stale targets cause stale output: Missing or outdated browserslist configuration can emit unnecessary prefixes or omit needed ones. Verify against your actual traffic, not assumptions.
- Imperfect polyfills: Some newer CSS features have limited plugin fidelity. Check behavior in your target browsers instead of assuming full support.
Check the result before you ship
Open dist/style.css and search for a prefixed property you expect, such as -webkit- or -moz-. Confirm that modern syntax you wrote, like nested rules, appears as flat, compatible CSS in the output. Then run npx browserslist and compare the resolved list against your analytics. If the prefixes and targets match your audience, the pipeline is doing its job.
PostCSS removes the manual prefixing burden, but only if you keep the target list honest and treat the build output as something to inspect, not just generate.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.