PostCSS in Vite
Vite is a tool for developing and building websites. PostCSS is built into it: you do not install postcss or postcss-cli separately. If the project root has a postcss.config.js, Vite applies it to every stylesheet that JavaScript imports.
Creating a project
Create a Vite 8 project from the plain JavaScript template and install the plugin you need:
npm create vite@latest blog -- --template vanilla
cd blog
npm install
npm install --save-dev postcss-preset-envThe template already contains src/style.css and the import import './style.css' in src/main.js. You do not install the postcss package — it comes with Vite.
Adding plugins
Create postcss.config.js in the project root, next to vite.config.js (if there is none, next to package.json):
// postcss.config.js
import postcssPresetEnv from 'postcss-preset-env';
export default {
plugins: [postcssPresetEnv()],
};Replace the contents of src/style.css with styles for the post list. It uses @custom-media — a named media query that has a lesson of its own:
/* src/style.css */
@custom-media --tablet (width >= 768px);
.post-list {
display: grid;
gap: 16px;
}
@media (--tablet) {
.post-list {
grid-template-columns: 1fr 1fr;
}
}Run npm run dev and open http://localhost:5173: Vite passes the file through PostCSS on the first request and after every save. You restart only after editing postcss.config.js itself.
Building
npm run build writes a file like index-A2CrBKVv.css into dist/assets/:
.post-list{grid-gap:16px;gap:16px;display:grid}@media (width>=768px){.post-list{grid-template-columns:1fr 1fr}}Without postcss.config.js the file would still contain @custom-media and @media (--tablet), which browsers do not understand.
What Vite does by itself
| Job | Done by |
|---|---|
Joining @import | Vite, no postcss-import needed |
| Your plugins | PostCSS, from postcss.config.js |
| Minifying on build | Lightning CSS (the default in Vite 8) |
Lightning CSS is a fast CSS tool written in Rust. On build, Vite minifies CSS with it and also adapts the code to the build target (by default baseline-widely-available — versions of the major browsers from roughly the last two and a half years): it adds required prefixes and unwraps nesting. That is why a Vite project does not need cssnano.
You can also list plugins right in vite.config.js, in the css.postcss.plugins option, but a separate file is more common: other tools understand it too.
Tailwind CSS 4 and Lightning CSS
Tailwind CSS 4 connects to Vite with its own plugin @tailwindcss/vite, not through PostCSS. For build tools without such a plugin there is the @tailwindcss/postcss package — it goes into postcss.config.js like any plugin. Tailwind 4 handles prefixes and @import itself, so Autoprefixer and postcss-import are not needed next to it.
Vite can also replace PostCSS with Lightning CSS completely using css.transformer: 'lightningcss' (still experimental). Then postcss.config.js is not read.
Common mistake
Installing postcss-cli in a Vite project and running it next to npm run dev. The CSS is processed twice, and the page still loads the source file through the import in src/main.js. In Vite, postcss.config.js is enough — it does the rest.