Read More on Telerik Blogs
October 20, 2025 Web, React
Get A Free Trial

Static extraction in CSS-in-JS isn’t one-size-fits-all, and you don’t necessarily need traditional static extraction to build performant React apps. The best optimization is often the one that actually ships to production!

If you’ve worked with React, chances are that you’ve encountered the concept of CSS-in-JS. CSS-in-JS changed how we think about styling React components by enabling us to colocate styles with components. This allows us to leverage JavaScript’s full power for dynamic styling.

Some popular CSS-in-JS libraries used in industry are styled-components, Emotion, Astroturf and linaria.

See the previous post on The Ultimate Guide to Styling React Components for more details on the different approaches to styling React components.

Like any powerful tool, CSS-in-JS comes with trade-offs. Chief among them: performance overhead. In this article, we’ll explore how static extraction in CSS-in-JS can help mitigate these performance concerns, leading to faster and more efficient React applications.

The Problem We’re Solving

When we write something like this with styled-components (for example):

const Button = styled.button`
  background-color: #007bff;
  color: white;
  padding: 12px 24px;
  border-radius: 4px;
  
  &:hover {
    background-color: #0056b3;
  }
`;

What happens at runtime (i.e., when your code actually runs in the browser)? The styled-components library needs to parse the above template literal, generate a unique class name, inject the styles into the DOM, and attach that class to your component. Multiply that by every styled component in your app, and you can start to see where performance might become an issue.

Note: While we’ll use styled-components to illustrate the concept of static extraction throughout this article, it’s worth noting that styled-components doesn’t actually support traditional static CSS extraction. This is a deliberate design choice we’ll explore in detail later. However, understanding how static extraction would work with styled-components helps us grasp the broader concept and appreciate the different approaches taken by various CSS-in-JS libraries.

If we take a look at the above button example again, we’ll notice the styles are completely static (i.e., they don’t change based on runtime conditions). Yet, (generally speaking) they’re still computed at runtime.

Static Extraction

Static extraction addresses this issue by analyzing CSS-in-JS code during build-time (i.e., when Vite, webpack or a similar bundler processes the code) to identify styles that don’t depend on props or state. Since these styles are considered static, they get “extracted” into regular CSS files, meaning our React app no longer needs to execute JavaScript for these styles at runtime!

In other words, instead of computing static styles in the browser, we precompute them and include them in regular CSS files. Our components still work exactly the same way, but now the browser doesn’t have to do all that work.

With this, we get the best of both worlds. We can keep writing CSS-in-JS the way we’re used to, but static styles get optimized away into regular CSS. Dynamic styles (i.e., the ones that actually need runtime computation) stay as CSS-in-JS.

Setting Up Static Extraction

Most CSS-in-JS libraries that support static extraction do it through Babel plugins or build-time transformations. Let’s look at how to set this up for styled-components. We’ll first install the babel-plugin-styled-components plugin:

npm install babel-plugin-styled-components

Then configure it in our Babel setup:

// babel.config.js
module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
  plugins: [
    [
      'babel-plugin-styled-components',
      {
        displayName: true,  // Better debugging
        pure: true          // Enables static extraction
      }
    ]
  ]
};

That pure: true option tells the plugin to analyze our styled components and extract any styles that don’t depend on props or theme values. The extracted styles end up in CSS files that get loaded alongside our JavaScript bundles. This essentially achieves the same performance goal as traditional static CSS extraction, reducing runtime overhead and improving overall application efficiency.

Static Extraction & Dynamic Styles

You might be wondering: What happens when styles are partially dynamic? This is where static extraction gets clever. Consider this component:

const Card = styled.div`
  background: white;
  border-radius: 8px;
  padding: 24px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  
  ${props => props.highlighted && `
    border: 2px solid #007bff;
    box-shadow: 0 4px 16px rgba(0, 123, 255, 0.2);
  `}
`;

A smart static extraction setup will pull out the base styles (background, border-radius, padding and default box-shadow) into static CSS. The conditional styles that depend on the highlighted prop remain as runtime CSS-in-JS. You get optimized performance for the common case while maintaining full dynamic capabilities.

Different Libraries, Different Approaches

The CSS-in-JS ecosystem has evolved different philosophies around static extraction. Let’s spend a little time in this section going into more detail on how these different libraries approach the concept of static extraction.

styled-components

Though we used styled-components as the example to illustrate the concept of static extraction above, styled-components doesn’t officially support traditional static extraction. As one project member (co-creator) explained in a GitHub issue, they made this choice deliberately:

“We don’t support static CSS extraction… Static extraction doesn’t generate dynamic CSS, which means your page will either appear broken until the JS executes, or you’ll need to defer until the JS is loaded.”

Instead, styled-components focuses on:

  • Server-side rendering (SSR) that sends only critical CSS for the initial render
  • Efficient runtime injection with batching and optimization
  • Maintaining style ordering for predictable specificity

The pure: true option in babel-plugin-styled-components doesn’t actually extract CSS files; it marks components as side-effect free for better tree-shaking and dead code elimination.

Emotion

Emotion initially supported static extraction but deprecated it in version 10. Their reasoning was pragmatic:

“As emotion has gotten more performant and features such as composition
have been added, static extraction has become less important… Libraries
such as linaria do static extraction well and the people working on them
are focused on this specific problem.”

When Emotion did support extraction, it worked like this:

// .babelrc
{
  "plugins": [["emotion", { "extractStatic": true }]]
}

This would generate separate .emotion.css files for styles without interpolations. However, it broke composition patterns and limited the library’s flexibility.

Linaria

Linaria takes a completely different approach—it’s zero-runtime CSS-in-JS. Everything is extracted at build time:

import { css } from '@linaria/core';
import { styled } from '@linaria/react';

const button = css`
  background-color: #007bff;
  color: white;
`;

const Button = styled.button`
  padding: 12px 24px;
  border-radius: 4px;
`;

Linaria compiles this to pure CSS files with no runtime overhead whatsoever. It even provides a collect helper for critical CSS extraction:

import { collect } from '@linaria/server';

const { critical, other } = collect(html, css);
// critical: CSS needed for initial render
// other: CSS that can be loaded async

Astroturf

Astroturf sits somewhere between Linaria and traditional CSS-in-JS, offering multiple APIs for different use cases:

import { css, stylesheet } from 'astroturf';

// Single class extraction
const btnClass = css`
  color: blue;
  border: 1px solid blue;
`;

// Full stylesheet extraction
const styles = stylesheet`
  .btn {
    padding: 0.5rem 1rem;
  }
  
  .primary {
    background-color: blue;
  }
`;

For dynamic values, Astroturf cleverly transpiles interpolations to CSS custom properties:

function Button({ bgColor }) {
  return (
    <button
      css={css`
        background-color: ${bgColor};
      `}
    >
      Click me
    </button>
  );
}
// Compiles to: background-color: var(--bgColor);

So, Should You Extract or Not? (Spoiler: It Depends)

Static extraction in CSS-in-JS isn’t a one-size-fits-all solution. The ecosystem has evolved different approaches based on different priorities. styled-components chose developer experience and flexibility over extraction. Emotion tried extraction but found it limiting. Linaria and Astroturf went all in on build-time extraction.

The key insight? You don’t necessarily need traditional static extraction to build performant React apps. Modern CSS-in-JS libraries are pretty fast, and techniques like SSR, code splitting and critical CSS can often provide better real-world performance than naive static extraction.

Here’s how you can think about picking a styling option:

Go with styled-components + SSR when you value developer experience above all, you’re already doing server-side rendering, or your app has moderate performance requirements. The developer experience is unmatched, and the performance is good enough for most apps.

Choose Linaria or Astroturf when zero runtime overhead is critical, you’re building something performance-sensitive (think ecommerce or news sites), or you’re comfortable with build-time constraints. The trade-off is less flexibility for dynamic styling, but for many applications, that’s worthwhile.

Stick with CSS Modules or vanilla CSS when you want complete control over extraction, prefer separation of concerns or runtime CSS-in-JS feels like bringing a bazooka to a knife fight.

The future likely holds a middle ground: build tools smart enough to automatically extract what can be extracted while preserving the flexibility we’ve come to expect from CSS-in-JS.

Until then, choose the approach that best fits your project’s needs, and remember: The best optimization is often the one that actually ships to production!


About the Author

Hassan Djirdeh

Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.

Related Posts