emotion, styled-components, styled-system, and theme-ui are JavaScript libraries that enable dynamic, component-scoped styling in React applications using CSS-in-JS patterns. They support theming, responsive design, and design system constraints, but differ significantly in architecture, API surface, and integration approach. emotion and styled-components focus on runtime or compile-time CSS generation with tagged template literals or object styles, while styled-system provides a utility-based prop system for applying design tokens (like spacing, colors, and typography) to components. theme-ui combines styled-system's prop-based API with Emotion under the hood, offering a full opinionated framework for building themeable UIs with Markdown support and built-in accessibility features.
When building modern React applications with consistent design systems, developers often reach for CSS-in-JS solutions that go beyond basic inline styles. The libraries emotion, styled-components, styled-system, and theme-ui each offer different philosophies for managing styles, themes, and responsive behavior. Let’s compare how they solve real-world problems.
emotion supports multiple syntaxes — you can use tagged templates or object styles, with or without React.
// emotion: string syntax
import { css } from '@emotion/react';
const button = css`
background: blue;
padding: ${props => props.theme.space[2]}px;
`;
// emotion: object syntax
const card = css({
borderRadius: '4px',
boxShadow: t => t.shadows[1]
});
styled-components uses only tagged template literals and requires React.
// styled-components: only string templates
import styled from 'styled-components';
const Button = styled.button`
background: ${props => props.theme.colors.primary};
padding: ${props => props.theme.space[2]}px;
`;
styled-system doesn’t render CSS — it provides functions that return style objects based on props.
// styled-system: style functions
import { space, color } from 'styled-system';
const Box = props => <div style={{ ...space(props), ...color(props) }} />;
// Usage: <Box p={3} bg="primary" />
theme-ui uses Emotion under the hood but exposes styled-system props directly on JSX elements.
// theme-ui: prop-based styling
/** @jsxImportSource theme-ui */
<div sx={{ p: 3, bg: 'primary', borderRadius: 2 }} />
All four support theming, but the mechanics differ.
emotion uses a ThemeContext but doesn’t require it — you can pass themes manually or use the useTheme hook.
// emotion theming
import { ThemeProvider, useTheme } from '@emotion/react';
const ThemedButton = () => {
const theme = useTheme();
return <button css={{ color: theme.colors.primary }} />;
};
styled-components requires wrapping your app in a ThemeProvider to access theme values inside styled components.
// styled-components theming
import { ThemeProvider } from 'styled-components';
const Button = styled.button`
color: ${props => props.theme.colors.primary};
`;
styled-system expects a theme object to be passed as props or via context (but doesn’t provide the context itself).
// styled-system theming (manual)
const theme = { space: [0, 4, 8, 16], colors: { primary: 'blue' } };
<Box theme={theme} p={2} color="primary" />
theme-ui mandates a global ThemeProvider and uses a strict theme specification (based on System UI Theme Spec).
// theme-ui theming
import { ThemeProvider } from 'theme-ui';
const theme = { space: [0, 4, 8], colors: { primary: 'tomato' } };
<ThemeProvider theme={theme}>
<div sx={{ color: 'primary' }} />
</ThemeProvider>
emotion and styled-components handle responsiveness through custom logic in their style definitions.
// emotion responsive
const Card = styled.div`
padding: 8px;
@media (min-width: 768px) {
padding: 16px;
}
`;
// Or with object syntax
const card = css({
padding: 8,
'@media (min-width: 768px)': { padding: 16 }
});
styled-system introduces array-based responsive props.
// styled-system responsive
<Box width={[1, 1/2, 1/3]} /> // full width on mobile, half on tablet, third on desktop
theme-ui inherits this array syntax and extends it with the sx prop.
// theme-ui responsive
<div sx={{ width: ['100%', '50%', '33%'] }} />
emotion supports zero-runtime CSS extraction via Babel plugin — styles become static <style> tags with no JavaScript overhead in production.
// With @emotion/babel-plugin, this becomes static CSS
const Button = styled.button`
color: hotpink;
`;
styled-components always ships runtime code to interpolate dynamic styles and manage component IDs. No zero-runtime option exists.
styled-system is pure utility functions — no runtime cost beyond function calls, but you still pay the cost of your underlying CSS-in-JS engine.
theme-ui uses Emotion internally, so it can benefit from Emotion’s zero-runtime mode if configured, but by default runs in runtime mode.
emotion is framework-agnostic — it works in React, Vue, vanilla JS, or even server-side rendering without React.
styled-components is React-only and deeply tied to React’s context and lifecycle.
styled-system is completely unopinionated about rendering — it’s just a set of functions that return style objects. You can use it with Emotion, Styled Components, or even plain React style props.
theme-ui is React-focused and includes extra features like MDXProvider for styling Markdown content consistently with your theme.
// theme-ui + MDX
import { MDXProvider } from '@mdx-js/react';
import { useThemeUI } from 'theme-ui';
const components = {
h1: props => <h1 sx={{ fontSize: 5, color: 'primary' }} {...props} />
};
<MDXProvider components={components}>
<MarkdownContent />
</MDXProvider>
As of 2024, all four packages are actively maintained:
emotion is widely used in large ecosystems (including MUI)styled-components remains popular with steady updatesstyled-system is stable and foundational to many design systemstheme-ui continues to evolve with support for modern React featuresNone are deprecated, but note that styled-system is not a standalone styling solution — it must be combined with a CSS-in-JS library.
| Feature | emotion | styled-components | styled-system | theme-ui |
|---|---|---|---|---|
| Styling Syntax | Strings or objects | Strings only | Style functions (objects) | sx prop (object + arrays) |
| Framework Support | Any (React optional) | React only | Any | React only |
| Theming Required? | No | Yes (for dynamic styles) | No (but expected) | Yes |
| Zero-Runtime Mode | ✅ (with Babel plugin) | ❌ | N/A (depends on engine) | ✅ (via Emotion) |
| Responsive Props | Manual media queries | Manual media queries | ✅ (array syntax) | ✅ (array syntax in sx) |
| Best For | Flexibility & performance | Pure component styling | Building custom design libs | Content sites with MDX |
Remember: styled-system is not a replacement for the others — it’s a complementary layer. In fact, theme-ui is essentially “Emotion + Styled System + conventions.” Choose based on whether you want flexibility (emotion), purity (styled-components), composability (styled-system), or convention (theme-ui).
Choose emotion if you need maximum flexibility in styling approaches — it supports both string-based and object-based styles, works with or without React, and offers zero-runtime extraction via @emotion/babel-plugin. It’s ideal for performance-sensitive applications or when integrating into non-React environments, as it doesn’t require a ThemeProvider for basic usage and allows fine-grained control over CSS injection and caching.
Choose styled-components if you prefer a purely component-centric styling model with strong encapsulation and automatic vendor prefixing. It enforces a clear boundary between styled components and logic components, and its theming API is tightly integrated via React context. However, it requires a ThemeProvider for theme access and has slightly higher runtime overhead due to its reliance on React context for every styled component.
Choose styled-system if you’re building a design system from scratch and want low-level, composable style props (like space, color, fontSize) that map directly to theme scales. It’s framework-agnostic and works with any component library, but it doesn’t generate CSS by itself — you must pair it with a CSS-in-JS engine like Emotion or Styled Components. Avoid using it alone; it’s a utility layer, not a complete styling solution.
Choose theme-ui if you want an opinionated, all-in-one solution that combines Emotion, styled-system props, theme-based configuration, and MDX support out of the box. It’s excellent for content-heavy sites (like blogs or documentation) where you need consistent styling across both React components and Markdown-rendered content. However, its conventions may feel restrictive if you need deep customization beyond its prop-based API.
ERROR: No README data found!