Was looking at the styling tools for Next.js that are App router compatible, and there still isn't an ideal solution for me..
– CSS modules: no child selectors like it's 2005
- SCSS modules: child selectors work but need wrapping every child class in :global() to disable
After updating my site, I thought I'd write up some more thoughts here.
TLDR: trying to replace the great DX of EmotionCSS with an RSC friendly alternative is currently not possible
Firstly, TailwindCSS
If you enjoy using TailwindCSS, you're in luck. As it's a compile time tool it works flawlessly with server components.
I enjoy TailwindCSS and use it on all projects, but I also prefer to use a more traditional styling tool for the majority of my components.
What styling tool has the best DX?
For me, it's got to be EmotionCSS 👩🎤. Sure CSS-in-JS have inherent performance downsides, but the DX was unbeatable.
Look at this example, it has everything (note in your IDE the css template literal would have syntax highlighting):
// 🔹 resuable composable stylesconst reuasableStyle = css` font-weight: 600;`// 🔹 conditional styles based on props and variablesfunction sectionTitleStyles(props: SectionTitleProps) { const localBoolExample = true const { large } = props return css` ${reuasableStyle} font-size: ${large ? '18px' : '14px'}; ${large && css` padding: 50px; background: papayawhip; /* 🔹 nested conditions */ border: ${localBoolExample ? `2px solid red` : '2px solid orange'}; `} /* 🔹 automatic styling of children */ span { color: green; } `}type SectionTitleProps = { large?: boolean children: React.ReactNode postFix?: string}// Look at how clean the component is without any conditional increasing its complexityexport const SectionTitle = (props: SectionTitleProps) => { const { children, postFix } = props return ( <h3 css={sectionTitleStyles(props)}> {children} {postFix && <span>{postFix}</span>} </h3> )}
My criteria for the ideal styling tool:
CSS syntax (not object styles) - this is a must, I'm not a fan of object styles.
Supports nested styles - SCSS has supported nesting since 2004, this is a non-negotiable (looking at you CSS modules)
Reusuable composable styles - super useful for sharing chunks of styles, keeping everything modular.
Conditional styles based on passed props - passing props into styles directly removes any conditional styling or class toggling from the component itself.
Conditional styles based on variables, nested being a bonus, and full power of JS inside styles
Automatic styling of children with only applying the component styles at the top level. This means not having to assign every style declaration to every element manually like CSS modules. This comes with added risk, but its minimal if you're aware of it and I believe the speed of development benefits outweigh the risk.
Not styled components. Turning a tag into a react component just to add styles is needlessly complex and confuses what are functional react components (with state and props etc) and what are plain tags with styles.
EmotionCSS meets all of these criteria. But its not SC friendly ❌
It's worth noting you won't ever be able to pass dynamic props to styles in any SC friendly styling tool. As it's just not possible to use dynamic values to build time static CSS files. Instead you'll need to use typical style tags, and manually apply the dynamic styles.
So what are the other options
Well there are a lot that are object style based like PandaCSS. But I'm not a fan of object styles.
So what other options are there that allow CSS syntax:
SCSS
I've used SCSS for years, its epic. Makes me nostalgic for CSS Guidelines. But unfortunately it doesn't automatically style children. You need to manually apply each style to each element. Or child selectors work but need wrapping every child class in :global() to disable generated classnames, which makes the CSS really verbose and hard to read.
CSS modules
Does not support nested styles
EmotionCSS
Only works for client components
PigmentCSS
Almonst perfect (and is powering this site)!
Linaria
I need to look into this and see if its viable!
PigmentCSS - nearly perfect, but not quite
PigmentCSS is a "zero-runtime CSS-in-JS library that extracts the colocated styles to their own CSS files at build time.". It's actually built on top of EmotionCSS, which is promising as they should echo the DX of EmotionCSS.
Unfortunately whilst it supports CSS Syntax (and that is how I use it for this site), they don't have much documentation for it. Theres even a bug discussing this.
The other major issue is that the CSS Syntax doesn't work in client components. Theres a bug discussing this.
It's also, at time of writing, not been updated since January 2025, so it's not very active.
Linaria - my next attempt
This is a promising tool, been around for a while and recently updated. I need to look into this and see if its viable. I'm hoping it solves my PigmentCSS issues of client components and CSS Syntax.
Josh Comeau is a big fan of it, and it's worked for him in a SC environment, so I'm hopeful.
I'll update this post with my findings over the next month. Watch this space! 😃
If you like React, Next.js or front-end development in general, feel free to follow and say hi on Twitter @_AshConnolly! 👋 🙂