Compare CSS Modules and runtime CSS-in-JS libraries across DX, performance, and dynamic styling

This tests build-time vs runtime style architecture trade-offs. A strong answer contrasts CSS Modules' static extraction with CSS-in-JS's prop-driven runtime injection, covers dynamic styling patterns, and weighs SSR and bundle costs.
What's really being asked
Architectural judgment on when to pay runtime costs for styling flexibility. Interviewers want to see you understand the boundary between build-time static extraction and client-side dynamic generation, and how that boundary affects performance budgets, SSR behavior, and team workflows.
The full answer
Four areas in order. First, the mechanism difference: CSS Modules are processed by the bundler into hashed class names and extracted into static CSS files, while runtime CSS-in-JS libraries like styled-components parse template literals and inject style tags on the client. Second, developer experience trade-offs: CSS Modules leverage existing PostCSS or Sass toolchains and keep styles in standard CSS syntax, whereas CSS-in-JS co-locates styles with components and lets you interpolate props and theme objects directly. Third, dynamic styling capabilities: CSS Modules handle variants by conditionally applying classes and can use CSS custom properties for runtime values, while CSS-in-JS can compute arbitrary style rules from props at runtime but pays a per-render cost. Fourth, performance and SSR implications: CSS Modules add zero JavaScript runtime overhead and produce cacheable CSS assets, while CSS-in-JS increases bundle size, requires runtime parsing, and complicates server-side rendering because styles must be collected and rehydrated.
The mistakes people make
Claiming CSS-in-JS has no runtime cost or that CSS Modules cannot do dynamic styling. Another red flag is asserting that one approach is always superior; senior candidates should map choices to constraints like team CSS fluency, performance budgets, and whether the app is heavily server-rendered. Also, confusing CSS Modules with plain global CSS shows a gap in understanding scoped styling.
What usually comes next
How would you migrate a large styled-components codebase to CSS Modules without losing prop-based variants? In what scenarios do CSS variables in CSS Modules outperform runtime injection? How do newer zero-runtime CSS-in-JS libraries like Linaria change this comparison? What caching strategies differ between a static CSS chunk and a runtime-generated style tag?
A concrete example
Consider a product catalog with frequent A/B tested color themes. With CSS Modules, you might define a theme.css of custom properties and toggle a data attribute on the root, letting the browser switch themes instantly with zero JS re-execution. With styled-components, you could pass a theme object through React context and let each component recompute its background and border colors from props, which is ergonomic but forces React re-renders and style recalculation across the tree.
Interview question
You are building a heavily server-rendered product catalog where themes must switch instantly without triggering React re-renders. Which approach best satisfies these requirements?
- a.Runtime CSS-in-JS using only static template literals to eliminate per-render costs
- b.CSS Modules with per-theme hashed classes applied conditionally via component props
- c.CSS Modules combined with CSS custom properties toggled by a data attribute on the root elementCorrect
- d.styled-components with a theme object passed through React Context
Why? this is the answer
CSS Modules with CSS custom properties let the browser switch themes instantly with zero JavaScript re-execution or React re-renders. The most tempting distractor, CSS Modules with conditional classes, still requires React to swap hashed class names on the root, violating the zero re-render constraint.
Just read this? Test yourself on what you have been reading.
Read the original → asoasis.tech
- #css modules
- #css-in-js
- #styled-components
- #react
- #performance
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.
See open roles