emotion vs glamorous vs styled-components vs styled-jsx
CSS-in-JS Architectures: Runtime Injection vs. Static Extraction in React
emotionglamorousstyled-componentsstyled-jsxSimilar Packages:

CSS-in-JS Architectures: Runtime Injection vs. Static Extraction in React

emotion, glamorous, styled-components, and styled-jsx are libraries that allow developers to write CSS directly within JavaScript or JSX files, solving scope leakage and dependency management issues in large-scale React applications.

emotion and styled-components are runtime libraries that inject styles dynamically into the DOM using JavaScript, offering powerful dynamic styling based on props and theme contexts. glamorous was a popular wrapper around emotion that enforced a specific component-based API but is now deprecated.

styled-jsx takes a different approach by acting as a Babel plugin (often built into Next.js) that extracts CSS at build time and scopes it using unique hashes, resulting in zero runtime overhead for style injection. While the first three rely on JavaScript execution to apply styles, styled-jsx leverages compiler transformations to output standard CSS tags.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
emotion0---6 years agoMIT
glamorous03,621-08 years agoMIT
styled-components041,1132.03 MB155 days agoMIT
styled-jsx07,7821.03 MB83a year agoMIT

CSS-in-JS Deep Dive: Emotion, Styled-Components, Glamorous, and Styled-JSX

Writing CSS in JavaScript has become a standard pattern for building scalable React applications. It solves real problems like global namespace collisions, unused CSS accumulation, and tight coupling between components and their styles. However, not all CSS-in-JS libraries work the same way under the hood.

This comparison breaks down emotion, styled-components, glamorous, and styled-jsx by looking at how they inject styles, how they handle dynamic props, and their current maintenance status. We will focus on practical implementation details to help you decide which tool fits your architecture.

⚠️ Critical Status Check: The Deprecation of Glamorous

Before diving into features, we must address glamorous. This library was once a favorite because it offered a clean API on top of emotion. However, glamorous is officially deprecated and no longer maintained.

The maintainers have archived the repository and explicitly advise users to migrate to emotion or styled-components. Continuing to use glamorous in new projects introduces technical debt immediately, as it will not receive updates for security patches or compatibility with future React versions.

// ❌ DO NOT USE: Glamorous is deprecated
import glamorous from 'glamorous';
const Title = glamorous.h1({ color: 'blue' });

// βœ… MIGRATE TO: Emotion (which powers glamorous)
import { css } from '@emotion/react';
const Title = (props) => <h1 css={css({ color: 'blue' })} {...props} />;

Recommendation: If you encounter glamorous in an existing codebase, plan a migration to emotion immediately. For all new projects, skip this package entirely.

πŸ—οΈ Core Architecture: Runtime Injection vs. Build-Time Extraction

The biggest architectural difference in this group is when and how the CSS reaches the browser.

emotion and styled-components are runtime libraries. They ship JavaScript code to the browser that executes when your app loads. This JS calculates styles, generates unique class names, and injects <style> tags into the document head dynamically.

// emotion: Runtime injection
import { css } from '@emotion/react';

function Box({ color }) {
  // The style object is processed by JS at runtime
  const styles = css({ backgroundColor: color, padding: '1rem' });
  return <div css={styles}>Content</div>;
}

// styled-components: Runtime injection
import styled from 'styled-components';

const Box = styled.div` 
  background-color: ${props => props.color}; 
  padding: 1rem; 
`;
// The template literal is processed by JS at runtime

styled-jsx is a build-time solution. It acts as a Babel plugin (included by default in Next.js). It scans your JSX during the build process, extracts the CSS, hashes it, and outputs standard <style> tags in your HTML. No style injection logic runs in the browser.

// styled-jsx: Build-time extraction
function Box({ color }) {
  return (
    <div className="box">
      Content
      {/* The style tag is transformed and scoped at build time */}
      <style jsx>{`
        .box {
          background-color: ${color};
          padding: 1rem;
        }
      `}</style>
    </div>
  );
}

Trade-off: Runtime libraries (emotion, styled-components) offer more power for dynamic logic but add JavaScript bundle weight and execution time. Build-time tools (styled-jsx) offer better performance and smaller bundles but are less flexible for complex dynamic styling.

🎨 Defining Styles: Tagged Templates vs. Object Syntax

How you write your styles affects developer experience and tooling integration.

styled-components relies almost exclusively on tagged template literals. This feels very much like writing standard CSS, which lowers the learning curve for designers or developers coming from traditional CSS backgrounds.

// styled-components: Tagged templates
import styled from 'styled-components';

const Button = styled.button`
  background: ${props => props.primary ? 'blue' : 'white'};
  color: ${props => props.primary ? 'white' : 'blue'};
  font-size: 1.2rem;
  
  &:hover {
    opacity: 0.8;
  }
`;

emotion is hybrid. It supports tagged templates (via @emotion/styled) but also excels at object styles (via css prop). Object styles are just JavaScript objects, meaning you get full TypeScript autocomplete and can use JavaScript logic naturally without string interpolation.

// emotion: Object styles
import { css } from '@emotion/react';

function Button({ primary }) {
  return (
    <button
      css={css({
        background: primary ? 'blue' : 'white',
        color: primary ? 'white' : 'blue',
        fontSize: '1.2rem',
        '&:hover': { opacity: 0.8 }
      })}
    >
      Click Me
    </button>
  );
}

// emotion: Tagged templates (also supported)
import styled from '@emotion/styled';
const StyledButton = styled.button`
  background: ${props => props.primary ? 'blue' : 'white'};
`;

styled-jsx uses standard CSS syntax inside a <style> tag within your component. It feels like writing CSS in a single-file component (SFC) similar to Vue or Svelte.

// styled-jsx: Standard CSS in JSX
function Button({ primary }) {
  return (
    <button className={primary ? 'primary' : 'secondary'}>
      Click Me
      <style jsx>{`
        button {
          font-size: 1.2rem;
        }
        button.primary {
          background: blue;
          color: white;
        }
        button.secondary {
          background: white;
          color: blue;
        }
        button:hover {
          opacity: 0.8;
        }
      `}</style>
    </button>
  );
}

⚑ Dynamic Styling: Props and Theming

Real-world apps need styles that change based on state, user input, or global themes. Here is how each library handles passing data into styles.

styled-components passes props directly into the template literal function. It also has a robust ThemeProvider built-in.

// styled-components: Props and Theme
import { ThemeProvider } from 'styled-components';

const Box = styled.div`
  color: ${props => props.theme.text};
  border: 1px solid ${props => props.active ? 'red' : 'gray'};
`;

// Usage
<ThemeProvider theme={{ text: 'black' }}>
  <Box active={true}>Content</Box>
</ThemeProvider>;

emotion handles props similarly in tagged templates but allows you to use props directly in object styles as well. Its theming works via a ThemeContext which you can consume with the useTheme hook or the withTheme HOC.

// emotion: Props and Theme
import { css } from '@emotion/react';
import { useTheme } from '@emotion/react';

function Box({ active }) {
  const theme = useTheme();
  return (
    <div
      css={css({
        color: theme.text,
        border: `1px solid ${active ? 'red' : 'gray'}`
      })}
    >
      Content
    </div>
  );
}

styled-jsx handles dynamic values by interpolating JavaScript expressions directly into the CSS string. However, it does not have a built-in global theming engine like the others. You typically manage themes using React Context and pass values down as props.

// styled-jsx: Dynamic interpolation
function Box({ active, textColor }) {
  return (
    <div className="box">
      Content
      <style jsx>{`
        .box {
          color: ${textColor};
          border: 1px solid ${active ? 'red' : 'gray'};
        }
      `}</style>
    </div>
  );
}

🌐 Server-Side Rendering (SSR) and Performance

If your app renders on the server (using Next.js, Remix, or custom setups), how styles are extracted matters for performance and avoiding "flash of unstyled content" (FOUC).

styled-components and emotion both require a specific setup to collect styles during server rendering and inject them into the HTML head before sending the response. They provide helper functions (StyleSheetManager for styled-components, CacheProvider for emotion) to handle this.

// styled-components SSR setup (simplified)
import { renderToString } from 'react-dom/server';
import { ServerStyleSheet } from 'styled-components';

const sheet = new ServerStyleSheet();
const html = renderToString(
  sheet.collectStyles(<App />)
);
const styleTags = sheet.getStyleTags();
// Inject `styleTags` into your HTML template

styled-jsx shines here. Because it extracts CSS at build time, the server-rendered HTML already contains the necessary <style> tags or links. There is no extra JavaScript step required to collect and inject styles on the server, making it faster and simpler to configure in Next.js environments.

// styled-jsx SSR
// No special server-side code needed in your entry point.
// Next.js handles the extraction and injection automatically.
// The rendered HTML simply contains:
// <style data-jsx="hash">.box{color:red}</style>

🀝 Similarities: Shared Ground

Despite their differences, these libraries share common goals and patterns.

1. πŸ”’ Scoped Styles by Default

All four libraries ensure that class names are unique and scoped to the component. You don't need to worry about naming conventions like .btn-large conflicting with other files.

// All libraries generate unique hashes internally
// emotion: css-1x2y3z
// styled-components: sc-bdVaJa
// styled-jsx: jsx-1a2b3c
<div className="css-1x2y3z">Safe from global collisions</div>

2. βš›οΈ React Integration

They all treat styles as first-class citizens in the React component lifecycle. Styles update automatically when props or state change.

// Common pattern: Conditional styling via props
const Component = ({ isActive }) => (
  <div className={isActive ? 'active' : ''}>...</div>
);

3. πŸ› οΈ Source Map Support

In development mode, emotion, styled-components, and styled-jsx all generate source maps. This allows you to click on a class name in browser DevTools and jump directly to the line in your JavaScript/JSX file where it was defined.

πŸ“Š Summary: Key Differences

Featureemotionstyled-componentsglamorousstyled-jsx
Statusβœ… Activeβœ… Active❌ Deprecatedβœ… Active
InjectionRuntime (JS)Runtime (JS)Runtime (JS)Build-Time (Babel)
SyntaxObjects & TemplatesTemplates OnlyObjectsStandard CSS
Bundle SizeMediumMediumMediumSmallest (Zero runtime)
Dynamic PropsHigh FlexibilityHigh FlexibilityHigh FlexibilityModerate (Interpolation)
ThemingBuilt-in ContextBuilt-in ProviderBuilt-inManual (React Context)
Best ForDesign Systems, FlexibilityRapid UI Dev, DXNoneNext.js, Performance

πŸ’‘ The Big Picture

Choosing the right tool depends on your project's constraints and priorities.

styled-components is the "batteries-included" choice. If you want a strong convention, excellent documentation, and an API that feels like writing CSS, this is the standard. It is perfect for teams that want to move fast and don't want to make many low-level decisions about how styles are structured.

emotion is the "flexible power-user" choice. If you love TypeScript, prefer object styles for better autocomplete, or need to share styles between React and non-React code, emotion is superior. It gives you the freedom to choose your own API pattern (styled vs. css prop) without losing features.

styled-jsx is the "performance-first" choice, specifically for Next.js users. If your styles are mostly static and you care deeply about bundle size and server-rendering performance, the build-time extraction of styled-jsx is unbeatable. The trade-off is less flexibility for complex dynamic logic.

glamorous has no place in modern development. Its deprecation is a clear signal to migrate away from it.

Final Thought: For most new greenfield projects today, the choice is often between emotion and styled-components for flexibility, or styled-jsx if you are deep in the Next.js ecosystem and prioritize raw performance. Avoid deprecated tools, and choose the syntax style (objects vs. templates) that makes your team most productive.

How to Choose: emotion vs glamorous vs styled-components vs styled-jsx

  • emotion:

    Choose emotion if you need a highly flexible, low-level CSS-in-JS solution that works seamlessly with both React and non-React projects. It is ideal for teams that want to mix string-based styles, object styles, and styled components without being locked into a single API pattern. Its robust theming support and framework agnosticism make it a safe bet for complex design systems.

  • glamorous:

    Do NOT choose glamorous for any new project. It has been officially deprecated and archived by its maintainers, who explicitly recommend migrating to emotion or styled-components. Using it introduces security risks and lacks support for modern React features like Concurrent Mode.

  • styled-components:

    Choose styled-components if you prefer a batteries-included, opinionated API that encourages building UIs as a hierarchy of styled primitives. It is best for teams that value a strong community ecosystem, extensive documentation, and features like automatic vendor prefixing and powerful server-side rendering hydration out of the box.

  • styled-jsx:

    Choose styled-jsx if you are building a Next.js application and prioritize performance and small bundle sizes over dynamic runtime styling features. It is the optimal choice for projects where styles are mostly static or rely on simple class switching, as it eliminates the JavaScript runtime cost associated with injecting styles in the browser.

README for emotion

ERROR: No README data found!