Setting Up Autoprefixer for Cross-browser Compatibility
Autoprefixer automatically adds vendor CSS prefixes based on data from Can I Use. A developer writes standard CSS — transform, flex, grid, appearance — Autoprefixer adds -webkit-, -moz-, -ms- where needed for specific browsers from the support list. Key: don't add prefixes manually; that's a deprecated approach.
How Autoprefixer Works
Autoprefixer is a PostCSS plugin. It reads .browserslistrc or the browserslist section in package.json, queries the Can I Use database, and adds only necessary prefixes:
/* Input (developer writes) */
.element {
display: flex;
appearance: none;
user-select: none;
backdrop-filter: blur(8px);
}
/* Output (Autoprefixer added) */
.element {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
Installation
npm install -D autoprefixer postcss
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
// Options are passed directly to the plugin or via browserslist
],
};
Browserslist — Key Configuration
Autoprefixer applies exactly as many prefixes as needed for specified browsers. Aggressive list → more prefixes → more CSS. Modern list → less clutter:
// .browserslistrc in project root
[production]
# Cover 95%+ of users
> 0.5%
last 2 major versions
Firefox ESR
not dead
not IE 11
not op_mini all
[development]
last 1 chrome version
last 1 firefox version
last 1 safari version
Check which browsers the list covers:
npx browserslist
npx browserslist "> 0.5%, last 2 versions, not dead"
# Check support for specific CSS property
npx browserslist --config .browserslistrc "backdrop-filter"
Integration in Bundlers
Vite
// vite.config.ts
import { defineConfig } from 'vite';
import autoprefixer from 'autoprefixer';
export default defineConfig({
css: {
postcss: {
plugins: [
autoprefixer({
// Override grid behavior
grid: 'autoplace', // or 'no-autoplace' for IE Grid
}),
],
},
},
});
Webpack + postcss-loader
// webpack.config.js
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
['autoprefixer', {
grid: false,
}],
],
},
},
},
Gulp (legacy projects)
// gulpfile.js
const { src, dest, watch, series } = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
function css() {
return src('src/styles/main.scss')
.pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
.pipe(postcss([
autoprefixer(),
...(process.env.NODE_ENV === 'production' ? [cssnano()] : []),
]))
.pipe(dest('dist/css'));
}
exports.css = css;
exports.watch = () => watch('src/styles/**/*.scss', css);
Configuration for Specific Cases
CSS Grid with IE 11
IE 11 supports the old Grid specification. Autoprefixer can transpile modern Grid to old syntax with caveats:
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({
grid: 'autoplace', // Enable IE Grid transformation
}),
],
};
/* Input */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
/* Output with IE 11 support */
.grid {
display: -ms-grid;
display: grid;
-ms-grid-columns: 1fr 1rem 1fr 1rem 1fr;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
However, full automatic conversion of complex Grid layouts doesn't always work correctly. If your project requires IE 11, testing is mandatory.
Safari and webkit-specific Properties
/* Autoprefixer will add for old Safari */
.modal {
position: sticky; /* → -webkit-sticky */
overscroll-behavior: none; /* → -webkit-overflow-scrolling */
}
.text-gradient {
background-clip: text; /* → -webkit-background-clip */
color: transparent; /* → -webkit-text-fill-color */
}
input[type="search"] {
appearance: none; /* → -webkit-appearance */
}
Debugging: Checking Results
# Show which prefixes will be added
npx autoprefixer --info
# Or via PostCSS CLI
npx postcss src/styles/main.css --use autoprefixer -o /dev/null --verbose
postcss-preset-env as an Extension
Autoprefixer covers only vendor prefixes. postcss-preset-env goes further — it polyfills new CSS features:
// postcss.config.js — recommended combination
module.exports = {
plugins: [
require('postcss-preset-env')({
stage: 2,
// autoprefixer is included inside preset-env by default
autoprefixer: {
grid: false,
},
features: {
'nesting-rules': true, // Native CSS nesting
'custom-media-queries': true, // @custom-media
'color-function': true, // color() function
'oklch-function': true, // oklch()
'is-pseudo-class': true, // :is()
'has-pseudo-class': true, // :has()
'custom-properties': false, // Keep native --vars
},
}),
],
};
Common Problems and Solutions
Autoprefixer adds legacy -moz- prefixes everywhere:
Update the database: npx browserslist@latest --update-db
Conflict between multiple postcss.config.js in monorepo:
Use postcss.config.cjs with explicit env parameter in each package.
Safari 15 doesn't support backdrop-filter without prefix:
Add Safari >= 14 to .browserslistrc — Autoprefixer will add -webkit-backdrop-filter.
appearance: none doesn't work on iOS:
Autoprefixer adds -webkit-appearance: none — ensure Browserslist includes the latest 2 versions of iOS Safari.
Timeline
Installing and setting up Autoprefixer with Browserslist: 30–60 minutes. Checking results and fine-tuning browser list: 1–2 hours. This is work done once during project initialization.







