CSS-in-JS: Pros and cons

Summary CSS-in-JS provides benefits like colocation, scoping, dynamic styling, and type-safety, for React-like frameworks. However, it can introduce runtime performance costs and added setup complexity. Modern libraries now offer static extraction and build support for SSR. Brief History CSS-in-JS emerged in 2014 with libraries like JSS and React’s inline styles, aiming to integrate styling with JavaScript for component-driven frameworks. The 2016 release of styled-components popularized the approach, offering a clean syntax and React integration. By 2018, Emotion and others expanded the ecosystem, addressing performance and flexibility. In server-side rendering (SSR) scenarios, CSS-in-JS libraries like styled-components and Emotion enable styles to be collected during rendering and embedded in the initial HTML response, ensuring faster page loads and preventing flash of unstyled content (FOUC). Static extraction libraries like Linaria (2020) and Vanilla Extract (2021) later minimized runtime costs. Why CSS-in-JS? Colocation: Styles stay with components, easing maintenance. Example using styled-components: import styled from 'styled-components'; const Button = styled.button` background: blue; color: white; padding: 10px; `; Scoping: Component-scoped styles prevent leaks. Emotion and styled-components generate unique class names, avoiding conflicts without BEM. Dynamic Styles: JavaScript enables runtime styles via props or state const Button = styled.button` background: ${props => (props.primary ? 'blue' : 'gray')}; font-size: ${props => props.size || '16px'}; `; Type-Safety: TypeScript catches errors early. interface ButtonProps { primary?: boolean; } const Button = styled.button` background: ${props => (props.primary ? 'blue' : 'gray')}; `; Why Not CSS-in-JS? Performance Overhead: Runtime style generation slows rendering versus static CSS. Static extraction mitigates but doesn’t eliminate this. Learning Curve: New APIs and tooling (Babel, Webpack) add complexity. Teams used to CSS or Tailwind may prefer simpler options. Modern CSS-in-JS Libraries Before Static Extraction styled-components: Template literals for React-friendly styling. Emotion: Lightweight, with CSS prop and TypeScript support. JSS: Object-based styles, less common in React. See: React: style-components Example After Static Extraction Linaria: Zero-runtime, styled-components-like syntax, extracts to CSS. Vanilla Extract: Type-safe, JavaScript objects, generates static CSS. Emotion (@emotion/css): Supports static extraction for build-time CSS. Panda CSS: Utility-first, static CSS with CSS-in-JS ergonomics. See: React: panda-css Example UI Libraries and CSS-in-JS Usage Material-UI (MUI): Uses Emotion (v5+), supports styled-components via plugin, JSS in older versions. Chakra UI: Emotion-based styled-system for dynamic, theme-driven styles. Mantine: Custom CSS-in-JS, Emotion-like, with static extraction. Ant Design: CSS Modules primarily, supports Emotion/styled-components via integrations. Radix UI: No CSS-in-JS; uses vanilla CSS or Tailwind for unstyled primitives. Conclusion CSS-in-JS offers colocation, scoping, dynamic styles, and type-safety, for React-like frameworks. Runtime performance and setup complexity are drawbacks. Static extraction tools like Linaria and Vanila Extract blend CSS-in-JS benefits with static CSS speed. UI libraries like MUI and Chakra UI favor Emotion, while Radix avoids CSS-in-JS for flexibility. Based on app scale and team skills; simplicity may favor CSS Modules or Tailwind.

Apr 11, 2025 - 18:02
 0
CSS-in-JS: Pros and cons

Summary

CSS-in-JS provides benefits like colocation, scoping, dynamic styling, and type-safety, for React-like frameworks. However, it can introduce runtime performance costs and added setup complexity. Modern libraries now offer static extraction and build support for SSR.

Brief History

CSS-in-JS emerged in 2014 with libraries like JSS and React’s inline styles, aiming to integrate styling with JavaScript for component-driven frameworks.

The 2016 release of styled-components popularized the approach, offering a clean syntax and React integration.

By 2018, Emotion and others expanded the ecosystem, addressing performance and flexibility. In server-side rendering (SSR) scenarios, CSS-in-JS libraries like styled-components and Emotion enable styles to be collected during rendering and embedded in the initial HTML response, ensuring faster page loads and preventing flash of unstyled content (FOUC).

Static extraction libraries like Linaria (2020) and Vanilla Extract (2021) later minimized runtime costs.

Why CSS-in-JS?

  1. Colocation: Styles stay with components, easing maintenance. Example using styled-components:
   import styled from 'styled-components';
   const Button = styled.button`
     background: blue;
     color: white;
     padding: 10px;
   `;
  1. Scoping: Component-scoped styles prevent leaks. Emotion and styled-components generate unique class names, avoiding conflicts without BEM.

  2. Dynamic Styles: JavaScript enables runtime styles via props or state

   const Button = styled.button`
     background: ${props => (props.primary ? 'blue' : 'gray')};
     font-size: ${props => props.size || '16px'};
   `;
  1. Type-Safety: TypeScript catches errors early.
   interface ButtonProps {
     primary?: boolean;
   }
   const Button = styled.button<ButtonProps>`
     background: ${props => (props.primary ? 'blue' : 'gray')};
   `;

Why Not CSS-in-JS?

  1. Performance Overhead: Runtime style generation slows rendering versus static CSS. Static extraction mitigates but doesn’t eliminate this.

  2. Learning Curve: New APIs and tooling (Babel, Webpack) add complexity. Teams used to CSS or Tailwind may prefer simpler options.

Modern CSS-in-JS Libraries

Before Static Extraction

  • styled-components: Template literals for React-friendly styling.
  • Emotion: Lightweight, with CSS prop and TypeScript support.
  • JSS: Object-based styles, less common in React.

runtime style injection

See: React: style-components Example

After Static Extraction

  • Linaria: Zero-runtime, styled-components-like syntax, extracts to CSS.
  • Vanilla Extract: Type-safe, JavaScript objects, generates static CSS.
  • Emotion (@emotion/css): Supports static extraction for build-time CSS.
  • Panda CSS: Utility-first, static CSS with CSS-in-JS ergonomics.

static css in a link

See: React: panda-css Example

UI Libraries and CSS-in-JS Usage

  • Material-UI (MUI): Uses Emotion (v5+), supports styled-components via plugin, JSS in older versions.
  • Chakra UI: Emotion-based styled-system for dynamic, theme-driven styles.
  • Mantine: Custom CSS-in-JS, Emotion-like, with static extraction.
  • Ant Design: CSS Modules primarily, supports Emotion/styled-components via integrations.
  • Radix UI: No CSS-in-JS; uses vanilla CSS or Tailwind for unstyled primitives.

Conclusion

CSS-in-JS offers colocation, scoping, dynamic styles, and type-safety, for React-like frameworks. Runtime performance and setup complexity are drawbacks. Static extraction tools like Linaria and Vanila Extract blend CSS-in-JS benefits with static CSS speed. UI libraries like MUI and Chakra UI favor Emotion, while Radix avoids CSS-in-JS for flexibility. Based on app scale and team skills; simplicity may favor CSS Modules or Tailwind.