The React community is experimenting with writing CSS in JavaScript—inline styles defined as objects, styling colocated with components, no separate stylesheets. This violates web development orthodoxy so completely that the reaction ranges from "interesting" to "what heresy is this?"
After building with CSS-in-JS approaches, I think the heresy might be onto something. But the trade-offs are real.
The Traditional Separation
Web development's trinity: HTML, CSS, JavaScript in separate files. Structure, presentation, behavior. Each layer independent, each technology in its domain.
This made sense when JavaScript sprinkled interactivity onto server-rendered pages. But SPAs broke the model. JavaScript creates HTML. JavaScript manages state. JavaScript coordinates everything. The separation became artificial.
CSS-in-JS says: if components own their logic and markup, why not their styles too?
const styles = {
button: {
backgroundColor: 'blue',
padding: '10px 20px',
border: 'none',
borderRadius: '3px'
}
};
<button style={styles.button}>Click Me</button>
Everything related to the button component lives together. Changing the button means editing one file, not hunting through stylesheets.
What CSS-in-JS Solves
Traditional CSS has pain points:
Global namespace: Every class is global. Naming conventions (BEM) help but require discipline. One typo, styles collide.
Dead code elimination: Hard to know if a CSS rule is still used. Deleting components leaves orphaned CSS.
Dependencies: Components depend on CSS classes. But there's no explicit connection—just convention and hope.
Load order matters: CSS cascade depends on order. Adding stylesheets in wrong order breaks things.
Minification challenges: Class name minification is complex because you can't know what's dynamic.
CSS-in-JS fixes these:
- Styles scoped to components automatically
- Delete component, styles gone
- Explicit dependencies via imports
- Order-independent (styles applied per component)
- Minification is straightforward
The React Inline Style Approach
React encourages inline styles:
const Button = ({ primary }) => {
const style = {
backgroundColor: primary ? 'blue' : 'gray',
padding: '10px 20px',
fontSize: '14px'
};
return <button style={style}>Click</button>;
};
Styles are JavaScript objects. Dynamic values are trivial (just use variables). State-dependent styling is clean (just use props/state).
But this approach has limitations:
- No pseudo-selectors (
:hover,:active) - No media queries
- No animations/keyframes
- Camel-cased property names (
backgroundColorvsbackground-color)
These limitations make pure inline styles insufficient for complex styling.
The Libraries Filling Gaps
Several libraries build on inline styles to add missing features:
Radium: Adds pseudo-selectors and media queries to inline styles
Aphrodite: Generates actual CSS from JavaScript objects, gets full CSS power
CSS Modules: Keep CSS in .css files but scope them locally via build-time transformation
Each solves different problems with different trade-offs. The ecosystem is experimenting.
The Performance Question
Does CSS-in-JS hurt performance?
Inline styles: Add weight to HTML (styles repeated per element). But avoid downloading separate stylesheets.
Generated CSS: Libraries like Aphrodite generate stylesheets dynamically. Adds JavaScript cost but gets CSS performance.
Critical CSS extraction: Some approaches extract critical CSS for first paint, defer rest. Complex but performant.
For most applications, the performance difference is negligible. Network and DOM operations dominate. Style approach rarely matters.
What You Lose
CSS-in-JS trades power for different benefits:
CSS tooling: Preprocessors (Sass, Less), PostCSS plugins, linters—you lose or must rebuild these.
Designer workflow: Designers comfortable with CSS files can't contribute as easily.
Established patterns: Years of CSS best practices don't transfer cleanly.
Standard syntax: camelCase JavaScript objects aren't standard CSS.
These losses are significant. CSS-in-JS isn't strictly better—it optimizes for different constraints.
When It Makes Sense
CSS-in-JS works well for:
- Component-heavy applications
- Teams prioritizing component encapsulation
- Dynamic styling needs (lots of state-dependent styles)
- Teams already comfortable with JavaScript-everything
Traditional CSS works better for:
- Content-heavy sites
- Teams with dedicated designers
- Projects valuing established CSS tooling
- Simpler styling needs
The Separation of Concerns Debate
The philosophical question: is CSS-in-JS violating separation of concerns?
Critics: Mixing styling and logic is wrong. CSS belongs in CSS files.
Proponents: Separation of technologies ≠ separation of concerns. Components are the unit of concern—they should own everything related to them.
Both arguments are valid. The disagreement is about what "concern" means. Is a button's appearance separate from its behavior? Or is "button" the concern that includes both?
There's no objective answer. It's preference and context.
Looking Forward
CSS-in-JS is early. Libraries are experimental. Patterns are emerging but not settled. This might be the future or a passing phase.
The component architecture trend suggests colocation has value. Whether styling belongs colocated with logic and markup depends on who you ask.
My take: CSS-in-JS makes sense for complex, component-heavy SPAs built by JavaScript-focused teams. Traditional CSS remains better for most other contexts.
The important thing is that we're questioning assumptions. Separation of concerns is good, but which concerns and how they're separated deserves examination.
CSS-in-JS is that examination in progress.
Resources:
- React Inline Styles – Official React approach
- Radium – Enhanced inline styles
- CSS Modules – Scoped CSS approach