Home / Articles

Moving Off Runtime CSS-in-JS: Server Components and Build-Time Styles

Why runtime CSS-in-JS clashes with React Server Components and performance goals, how Tailwind, CSS Modules and zero-runtime libraries compare, and how to migrate safely.

Publisher: REACTAPP.TOP5 min read1058 words

This article is published in English.

Sources: Why CSS-in-JS Is Dying in 2026: Performance, Server Components, and What to Use Instead

For several years, runtime CSS-in-JS libraries such as styled-components and Emotion were the go-to way to style React components: styles scoped to a component, values driven by props, and no global class name collisions. The ecosystem has since shifted toward styles that are generated at build time, pushed mainly by performance budgets and by React Server Components. This article explains what changed, compares the main alternatives, and gives you a practical rule for when to migrate an existing codebase and when to leave it alone.

Many teams, large and small, have already moved new work away from runtime styling, and the reasons matter before you choose an approach.

The cost of generating styles in the browser

Runtime CSS-in-JS libraries produce CSS while your app is running. As components render, the library turns style definitions (including any values that depend on props) into CSS rules, generates class names and injects <style> tags into the document. That work runs on the main thread, on the user's device. Published benchmarks comparing it to static CSS have reported rendering slowdowns in the range of 20 to 50 percent; the real impact depends heavily on the app, so treat such numbers as a signal to measure your own pages rather than as a universal figure.

The underlying issue is architectural rather than a bug in any one library: style computation is done in the browser instead of at build time. That means the styling engine itself ships in your JavaScript bundle, the browser has more script to parse and execute before the page becomes interactive, and style insertion competes with rendering for time.

Why Server Components exposed the weak spot

React Server Components (RSC) render on the server and send their output to the browser without shipping the component code as client JavaScript. Runtime CSS-in-JS depends on a JavaScript runtime that tracks and injects styles during rendering, and it relies on React context, which Server Components do not support. That puts it at odds with the server-first model used by frameworks such as the Next.js App Router.

In practice, using styled-components or Emotion with Server Components means marking the styled parts of your tree as Client Components with the 'use client' directive, usually together with extra setup to collect styles during server rendering. When styled primitives are used everywhere, a large share of the component tree ends up on the client. That undoes much of what Server Components offer, namely less JavaScript sent to the browser and less work there, purely to keep the styling library functional. For many teams, that trade-off has not been worth it.

The build-time alternatives

Runtime CSS-in-JS had its peak roughly between 2018 and 2023. Today there are several mature options that avoid the runtime cost, each suited to a different team preference.

Tailwind CSS as the common default

Tailwind CSS has become the usual starting point for new React and Next.js projects. You compose utility classes directly in markup, and the build step emits only the CSS for the classes you actually use. There is no styling runtime, the resulting stylesheet stays small, and because the output is plain CSS it works in Server Components without any special handling.

CSS Modules for teams that prefer plain CSS

If you would rather write ordinary CSS than utility classes, CSS Modules give you locally scoped class names with no runtime cost. They are supported out of the box by most bundlers and framework toolchains, so adopting them usually requires no configuration at all.

Zero-runtime CSS-in-JS for the familiar authoring model

Libraries such as Panda CSS and Vanilla Extract let you keep writing styles in JavaScript or TypeScript, much as you did with styled-components, but they extract everything into static CSS during the build. You keep the authoring experience and type safety, without runtime overhead, and with full compatibility with Server Components. The main adjustment is that styles must be statically analyzable, so values that change at runtime are typically expressed through variants or CSS custom properties instead of arbitrary props. The patterns for bridging JavaScript values into CSS variables are covered in our guide to CSS custom properties at runtime.

Deciding when to migrate

New projects

For a new project, there is little reason to choose runtime CSS-in-JS today. A reasonable default:

  • Tailwind CSS for most projects.
  • CSS Modules if the team prefers conventional stylesheets.
  • A zero-runtime CSS-in-JS library if you want to keep writing styles in TypeScript without paying for a runtime.

Existing projects

For an app already built on styled-components or Emotion, there is no need to rush. Both libraries still work well in client-rendered apps. Migration becomes worth planning when you intend to adopt Server Components or when profiling shows styling is a meaningful part of your rendering cost.

When you do migrate, do it incrementally, one component at a time, moving to Tailwind or CSS Modules. Leaf components and shared primitives like buttons are easy first targets. Both systems can coexist during the transition, but keep an eye on specificity and style ordering while old and new styles live side by side.

Wrapping up

  • Runtime CSS-in-JS solved real problems, but it computes styles on the client, which costs bundle size and main-thread time.
  • Server Components make that cost more visible, because the libraries depend on client-side JavaScript and push styled trees back to the client.
  • Tailwind CSS, CSS Modules and zero-runtime libraries such as Panda CSS and Vanilla Extract all generate static CSS at build time and work naturally with Server Components.
  • Choose a build-time approach for new work; for existing apps, migrate gradually and only when Server Components or measured performance give you a concrete reason.