CSS-in-JS
CSS-in-JS authors styles in JavaScript or TypeScript, usually right next to the component they style. The appeal is co-location, scoping without naming conventions, and styles that respond to props and themes as plain code.
The catch is cost: many popular libraries do styling work at runtime in the browser, which adds overhead and doesn't play well with React Server Components. That's pushed the ecosystem toward zero-runtime options that extract real CSS at build time.
TL;DR
- CSS-in-JS co-locates styles with components and scopes them automatically.
- It shines for dynamic, prop-driven styling and theming.
- Runtime libraries (styled-components, Emotion) add browser overhead; zero-runtime ones (vanilla-extract, Linaria, Panda) extract CSS at build time.
- Standardize on one approach per app, and prefer zero-runtime when performance or RSC matters.
Quick Example
A styled component whose styles react to props (Emotion / styled-components style):
Core Concepts
- Co-location — styles live with the component, not in a far-off stylesheet.
- Scoping — generated class names avoid global collisions automatically.
- Dynamic styling — styles are JavaScript, so they can depend on props and state.
- Theming — a theme object flows through context to every styled component.
Runtime vs Zero-Runtime
This is the distinction that matters most today:
Runtime CSS-in-JS has fallen out of favor for performance-sensitive and server-component apps; new projects increasingly choose zero-runtime CSS-in-JS or utility-first Tailwind.
Best Practices
- One approach per app — mixing several styling systems creates chaos.
- Prefer zero-runtime when performance or React Server Components are in play.
- Centralize design tokens (colors, spacing, type) in the theme.
- Avoid generating styles per render — define styled components once, outside the render path.
Common Mistakes
Creating styled components inside render
Ignoring runtime cost at scale
FAQ
Is CSS-in-JS dead?
No, but runtime CSS-in-JS has lost ground. Its browser overhead and incompatibility with React Server Components pushed the ecosystem toward zero-runtime libraries (vanilla-extract, Linaria, Panda) and utility-first Tailwind. The co-location idea lives on, mostly without the runtime cost.
What's the difference between runtime and zero-runtime?
Runtime libraries generate and inject styles in the browser as components render. Zero-runtime libraries extract static CSS files at build time, so the browser just loads plain CSS — faster and compatible with server components.
Does CSS-in-JS work with React Server Components?
Runtime libraries generally don't, because they rely on client-side context and render-time style injection. Zero-runtime libraries do, since they produce static CSS at build time. Pick a zero-runtime option for RSC apps.
CSS-in-JS or Tailwind or CSS Modules?
CSS-in-JS for heavily dynamic, prop-driven styling. Tailwind for fast, consistent utility-first styling. CSS Modules for plain CSS with local scoping and zero runtime. Many teams now combine Tailwind or CSS Modules with a zero-runtime extractor.
Related Topics
- Tailwind CSS — Utility-first alternative
- CSS Fundamentals — What every approach compiles to
- Sass/SCSS — Preprocessor styling
- React — Where component-scoped styles fit
- Web Performance — Why runtime cost matters