Configure PostCSS preset-env and autoprefixer for target browsers
Transform modern CSS syntax and add vendor prefixes with PostCSS, postcss-preset-env, and autoprefixer using explicit browserslist targets.
16 Jun 2026, 10:18 UTC

Problem and takeaway
If you write modern CSS such as nesting rules or custom media queries, older or stricter browsers may ignore those declarations. PostCSS can transform that syntax into broadly supported CSS and add vendor prefixes where needed. The useful takeaway is that plugin order and browser targets matter more than the plugin list itself: run postcss-preset-env before autoprefixer, and define explicit browserslist targets instead of relying on defaults.
Desired outcome
A build step that reads modern CSS from src/styles.css, transforms unsupported syntax, inserts vendor prefixes for your deployment targets, and writes the result to dist/styles.css.
Prerequisites
- A Node.js project with
package.jsonin the project root. - Terminal access to the project root with normal user permissions; no
sudois required. - PostCSS 8.x assumed. Pin plugin versions in
package.jsonbecause major releases can change default feature behavior.
Verify plugin versions against your installed PostCSS 8.x release before treating this configuration as final.
Install dependencies
Run in the project root:
npm install --save-dev postcss postcss-preset-env autoprefixer
This writes to node_modules and updates package.json. Risk: a failed install leaves partial dependencies; remove node_modules and package-lock.json and retry if the command errors.
Define browser targets
Both postcss-preset-env and autoprefixer read the same browserslist configuration. Add an explicit block to package.json:
"browserslist": [
"last 2 versions",
"not dead",
"> 0.5%"
]
Alternatively, create .browserslistrc with the same queries. Confirm the resolved targets with:
npx browserslist
Expected check: the list should include the browser versions you actually support. If it includes unexpected browsers, tighten the queries before building.
Write postcss.config.js
Create postcss.config.js in the project root. Export an object or function; the example below uses an object so the same pipeline applies to development and production.
module.exports = {
map: { inline: false, annotation: true },
plugins: [
require('postcss-preset-env')({
stage: 3,
features: {
'nesting-rules': true,
'custom-media': true,
'logical-properties-and-values': true,
},
}),
require('autoprefixer')(),
],
};
postcss-preset-env transforms syntax first. autoprefixer then prefixes the transformed output. Reversing this order can cause prefixes to be missed on properties created by preset-env transformations. Feature names may differ by preset-env major version; consult the documentation for the installed release.
Source maps are enabled with map: { inline: false, annotation: true }; browser DevTools can then point to the original source instead of generated CSS.
Add modern CSS and build
Create src/styles.css:
.card {
color: rebeccapurple;
&:hover {
color: darkblue;
}
}
@custom-media --viewport-medium (width >= 600px);
@media (--viewport-medium) {
.card {
padding: 1rem;
margin-inline-start: 1rem;
}
}
Run the PostCSS CLI from the project root:
npx postcss src/styles.css -o dist/styles.css
Expected check: open dist/styles.css and confirm:
- Nested rules are flattened, for example
.card:hover. @custom-mediais replaced with an expanded media condition.- Vendor prefixes such as
-webkit-appear only for properties your targets require. - A
dist/styles.css.mapfile exists next to the output.
Diagnostic decisions
| Symptom | Likely cause | Check |
|---|---|---|
| No vendor prefixes in output | Targets do not require them, or autoprefixer is missing | Run npx browserslist and confirm autoprefixer is in the plugin list |
| Prefixes on unprefixed properties | Targets are too broad | Tighten browserslist queries and rerun npx browserslist |
| Nesting or custom media unchanged | postcss-preset-env is missing or version changed defaults | Confirm plugin order and pinned version in package.json |
| DevTools shows generated CSS | Source maps disabled or missing | Confirm map option and the .map file |
Comparison: with and without autoprefixer
Build once with autoprefixer and once with only preset-env. If a property such as display: flex appears with display: -webkit-box in the first build but not the second, autoprefixer is active for your targets. If both outputs are identical, your targets likely do not require prefixes.
Limitations
- PostCSS transforms syntax at build time; it cannot polyfill features that require JavaScript runtime behavior. Use progressive enhancement or a JavaScript fallback for those cases.
- PostCSS does not validate CSS semantics beyond plugin scope. Invalid input may pass through unchanged or produce unexpected output.
- Implicit browserslist defaults can generate unnecessary prefixes or omit needed ones. Always define targets per deployment environment.
Rollback
This operation writes generated files to dist/. To roll back, delete dist/styles.css and dist/styles.css.map, or revert postcss.config.js and package.json with Git. No database or runtime state is changed.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.