My PostCSS Setup for Next.js Projects
Introduction
I've been using PostCSS with my Next.js projects for quite some time now.
I previously relied on Less and Sass, but with the continuous advancement of CSS and the introduction of features like custom properties, calc()
, Flexbox, and CSS Grid, I decided to shift my focus away from preprocessors.
The first time I heard about PostCSS was via Autoprefixer, but there's much more to PostCSS than Autoprefixer.
Once I understood how PostCSS could streamline my workflow, I was hooked.
In this brief guide, I'll share my go-to PostCSS setup that has proven to be invaluable for numerous Next.js projects, if you're coming from Sass or Less and are on the fence then you should find this useful.
PostCSS config
I always use a customized postcss config, I have my own preferences that are transferable to each project. Here's the config file I use for my personal website (this one):
Next.js includes a sensible default configuration that includes Autoprefixer. My config is 100% custom, when you opt for a custom config, Next.js completely disables theirs.
You'll notice quite a few things are configured, which I'll be covering through this guide:
- PostCSS Preset Env
- Autoprefixer
- Stages
- Features
- PostCSS Mixins
- PostCSS Functions
postcss preset env
Discovering postcss-preset-env was a game-changer for me. I first whet my appetite by using the ubiquitous Autoprefixer, which takes way the burden of adding vendor prefixes to CSS properties, like so:
While PostCSS serves as a bridge between CSS preprocessors like Sass and Less, PostCSS Preset Env take it up a notch by enabling modern CSS capabilities, while also providing polyfills for unsupported features.
Imagine being able to craft forward-compatible CSS without fretting over browser compatibility, thanks to PostCSS preset Env that's exactly what I can do!
So, instead of writing in Sass or Less, which are non-native, I've been writing CSS according to various draft CSS specifications. The progress of each specification is evaluated by what's known as a stage.
Here's a brief outline of these stages:
- 0: Aspirational - Early stage, unstable, unofficial drafts open for discussion.
- 1: Experimental - Recognized problem, unstable, no specific solution.
- 2: Allowable - Specific solution proposed, still unstable.
- 3: Embraced - Stable candidate recommendations, likely to become standard.
- 4: Standardized - W3C recommendation, implemented by all browser vendors.
- Rejected - Neglected or formally rejected specifications.
Typically, I opt for stage: 0
.
While there's always a chance that certain features may be discarded or that specifications might alter, everything has been smooth sailing so far.
CSS Features that I use with postcss-preset-env
Take the following code, written in Sass:
It works, but it isn't CSS, now take a look at how it can be re-written with CSS Nesting and Custom media, enabled with PostCSS Preset Env:
There are some notable differences, mainly the &
selector, in Sass you don't need it and after coming from Sass I originally found it confusing, but eventually got used to it.
I've been incorporating CSS Nesting for at-least 3 years as of the time of writing. It seems my gamble has paid off. The specification had little support in web browsers until 2023, but now more browser vendors are adding CSS nesting it's gaining traction and I won't have to rewrite the code I've been churning out, yay!
Without @custom-media
, maintaining DRY (Don't Repeat Yourself) principles for media query values becomes a challenge, which was one of the benefits of using Sass / Less.
Defining custom media can be done in a couple of different ways. If you define them in JS then you'll have a single source of truth for both CSS and JS breakpoints.
Bear in mind that you need to import either file in postcss.config.js
.
I tend to avoid switching components with JS so more recently have opted for defining them in CSS, like this:
PostCSS mixins
PostCSS mixins are another essential part of my setup. They enable me to create reusable chunks of CSS code, similar to what's possible with Sass or Less. This keeps my CSS organized and easier to maintain.
You can write useful little utilities such as the following clamp()
mixin:
Which you would reference like so:
Or a useful typography mixin like this one:
You'd then reference the mixin in another file.
You can go further and create mixins for buttons:
...which you would reference like:
PostCSS functions
PostCSS Functions is a plugin for exposing Javascript functions to CSS.
It's extremely useful for generating values, and is especially useful if you get tired of writing calc()
css functions, or anything else that seems a little verbose and repetitive.
Conclusion
Understanding PostCSS and integrating it into my Next.js projects has allowed me to take advantage of modern CSS features, while still maintaining compatibility with older browsers, not only that, but I've streamlined my workflow and made my projects more maintainable.
If you haven't yet tried PostCSS, I highly recommend giving it a go and exploring the benefits.
If you have any suggestions as to how the examples can be made clearer, easier to understand, or how I can add any missing use-cases you feel would help others, I'd love nothing more than to hear from you! Reach out to me on Twitter, or shoot me an email.
Written by Morgan Feeney
I’ve been designing and developing for almost 2 decades.
Read about me, and my work.
For juicy tips and exclusive content, subscribe to my FREE newsletter.