styled-components vs emotion vs theme-ui vs rebass
React CSS-in-JS 引擎与设计系统方案深度对比
styled-componentsemotiontheme-uirebass类似的npm包:

React CSS-in-JS 引擎与设计系统方案深度对比

emotion 和 styled-components 是 React 生态中最主流的 CSS-in-JS 底层引擎,负责将样式逻辑直接写入 JavaScript 组件中,提供样式隔离和动态样式能力。theme-ui 是基于 emotion 构建的高层设计系统库,通过约束性的 Theme 对象和 sx 属性简化组件样式开发。rebass 是早期的无头组件库,基于 styled-system,但目前已停止维护。这四者代表了从底层样式引擎到高层设计系统约束的不同抽象层级,开发者需根据项目对灵活性、约束性及维护状态的需求进行选择。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
styled-components11,064,46241,1212.08 MB198 天前MIT
emotion801,353---6 年前MIT
theme-ui63,1765,39880.4 kB628 个月前MIT
rebass28,0857,903-957 年前MIT

Emotion vs Styled-Components vs Theme-UI vs Rebass: 架构选型与实战对比

在 React 生态中,样式解决方案的选择直接影响代码的可维护性、性能以及团队协作效率。emotion 和 styled-components 提供了底层样式引擎,而 theme-ui 和 rebass 则在此基础上构建了更高层的设计系统约束。本文将深入对比它们的核心机制、API 设计以及维护状态,帮助架构师做出明智决策。

🎨 核心样式写法:模板字符串 vs 对象样式

styled-components 强制使用 tagged template literals(模板字符串)。

  • 样式写法接近传统 CSS,支持嵌套。
  • 适合习惯 CSS 语法的开发者,但动态样式需要通过函数插值。
// styled-components: 模板字符串
const Button = styled.button`
  background: ${props => props.primary ? 'blue' : 'white'};
  color: ${props => props.primary ? 'white' : 'blue'};
  padding: 8px 16px;
`;

emotion 同时支持模板字符串和对象样式。

  • 对象样式更适合 TypeScript 推断和动态逻辑。
  • 提供 css prop,无需包装组件即可应用样式。
// emotion: 对象样式 + css prop
import { css } from '@emotion/react';

function Button({ primary }) {
  return (
    <button css={css({
      background: primary ? 'blue' : 'white',
      color: primary ? 'white' : 'blue',
      padding: '8px 16px'
    })}>
      Click Me
    </button>
  );
}

theme-ui 基于 emotion,使用 sx 属性。

  • sx 属性直接映射 Theme 对象中的键值。
  • 样式代码更简洁,但依赖预定义的 Theme 结构。
// theme-ui: sx prop
import { Box } from 'theme-ui';

function Button({ primary }) {
  return (
    <Box
      as="button"
      sx={{
        bg: primary ? 'primary' : 'background',
        color: primary ? 'background' : 'text',
        p: 2
      }}
    >
      Click Me
    </Box>
  );
}

rebass 使用组件属性传递样式值。

  • 通过 color、p 等 props 直接控制样式。
  • 代码最简洁,但灵活性最低,且已停止维护。
// rebass: 样式 Props (Legacy)
import { Button } from 'rebass';

// 已弃用,仅展示历史写法
function LegacyButton({ primary }) {
  return (
    <Button
      bg={primary ? 'blue' : 'white'}
      color={primary ? 'white' : 'blue'}
      p={2}
    >
      Click Me
    </Button>
  );
}

🧩 主题系统:自由配置 vs 约束规范

styled-components 提供 ThemeProvider。

  • 主题结构完全由开发者定义,无强制规范。
  • 通过 props.theme 访问,灵活但容易导致团队间结构不一致。
// styled-components: 自由主题
const theme = { colors: { primary: 'blue' } };

const Title = styled.h1`
  color: ${props => props.theme.colors.primary};
`;

emotion 同样提供 ThemeProvider。

  • 机制与 styled-components 类似,但配合 css prop 使用更灵活。
  • 不强制主题结构,适合需要高度定制的项目。
// emotion: 自由主题
import { useTheme } from '@emotion/react';

function Title() {
  const theme = useTheme();
  return <h1 css={{ color: theme.colors.primary }}>Hello</h1>;
}

theme-ui 强制使用 Standard Theme Specification。

  • 主题键名(如 colors.primary, space[0])是标准化的。
  • 减少决策成本,但修改底层规范较困难。
// theme-ui: 标准主题
const theme = {
  colors: { primary: 'blue' },
  space: [0, 4, 8, 16]
};

// 组件自动读取 theme.space[2]
<Box p={2}>Content</Box>

rebass 基于早期的 styled-system 规范。

  • 与 theme-ui 类似,但版本较旧,不再更新规范。
  • 新项目使用会导致与现有生态脱节。
// rebass: 旧版规范 (Legacy)
// 类似 theme-ui,但不再推荐
<Box p={2}>Content</Box>

⚠️ 维护状态与未来风险

emotion 和 styled-components 均处于活跃维护状态。

  • 两者都有稳定的版本更新,支持 React 最新特性(如 Concurrent Features)。
  • 社区活跃,遇到问题容易找到解决方案。

theme-ui 处于维护模式,更新频率较低。

  • 基于 emotion,底层依赖稳定。
  • 适合已有项目,但新项目需评估其长期活跃度。

rebass 已官方标记为 不再维护 (Archived)。

  • 仓库已归档,不再接受 PR 或修复。
  • 绝对不要在新项目中使用,存在安全和技术债务风险。
// 风险提示
// import { Box } from 'rebass'; // ❌ 不要这样做
// import { Box } from 'theme-ui'; // ✅ 更安全的替代

🚀 性能与 SSR 支持

emotion 在 SSR 方面表现优异。

  • 提供 extractCritical 等工具,避免样式闪烁(FOUC)。
  • 运行时性能略优于 styled-components(在某些基准测试中)。
// emotion: SSR 提取
import { extractCritical } from '@emotion/server';

const { ids, css } = extractCritical(html);

styled-components 需要配合 ServerStyleSheet。

  • 配置稍复杂,但文档完善。
  • 性能足以支撑大多数企业级应用。
// styled-components: SSR
const sheet = new ServerStyleSheet();
const html = renderToString(sheet.collectStyles(<App />));
const styleTags = sheet.getStyleTags();

theme-ui 继承 emotion 的 SSR 能力。

  • 配置简单,只需包裹 ThemeProvider。
  • 性能取决于底层 emotion 的表现。
// theme-ui: SSR
// 沿用 emotion 的 SSR 配置即可

rebass 依赖旧版 styled-system。

  • SSR 配置可能与现代工具链不兼容。
  • 性能优化不再跟进最新浏览器特性。
// rebass: SSR
// 旧版配置,可能已过时

📊 核心特性对比总结

特性emotionstyled-componentstheme-uirebass
底层引擎自研自研基于 Emotion基于 Styled-System
样式语法对象 / 模板模板字符串sx 属性组件 Props
主题规范自由自由标准化约束标准化约束 (旧)
维护状态✅ 活跃✅ 活跃⚠️ 维护模式❌ 已弃用
TS 支持✅ 优秀✅ 优秀✅ 优秀⚠️ 一般
推荐场景组件库 / 高灵活企业应用 / 生态依赖设计系统 / 快速规范❌ 不建议使用

💡 架构师建议

emotion 是技术自由度最高的选择 🕊️。如果你正在构建一个需要发布给他人使用的组件库,或者团队对 TypeScript 和对象样式有强烈偏好,它是首选。它的底层能力最强,且被 theme-ui 等库验证为可靠基石。

styled-components 是生态最成熟的选择 🌳。如果团队希望减少配置时间,依赖丰富的第三方主题和教程,且偏好 CSS -like 语法,它依然非常稳健。适合大多数标准 Web 应用开发。

theme-ui 是设计系统落地的捷径 🎨。如果你希望强制团队遵循统一的空间、颜色和排版规范,减少样式争论,它是一个很好的上层抽象。但需接受其更新节奏较慢的现实。

rebass 是历史遗产 🏛️。仅存在于旧项目中。如果遇到它,请将其视为技术债务,计划迁移到 theme-ui 或原生 emotion 方案。

最终思考:CSS-in-JS 的核心价值在于样式与组件的共定位。选择 emotion 或 styled-components 作为底层引擎是安全的,而是否引入 theme-ui 这样的约束层,取决于你的团队是需要“自由”还是“规范”。

如何选择: styled-components vs emotion vs theme-ui vs rebass

  • styled-components:

    选择 styled-components 如果你偏好模板字符串语法,希望拥有最大的社区生态和插件支持,或者团队已经熟悉其开发模式。它在开发者体验(DX)上非常优秀,适合需要快速原型开发且依赖丰富第三方主题资源的企业级应用。

  • emotion:

    选择 emotion 如果你需要极高的灵活性,偏好对象样式语法,或者正在开发一个需要强大 SSR 支持和样式提取能力的组件库。它在性能优化和 API 多样性(css prop、styled、class names)方面表现最佳,适合对样式控制粒度要求极高的场景。

  • theme-ui:

    选择 theme-ui 如果你希望快速建立一套基于约束的设计系统,且不想手动处理底层 CSS-in-JS 配置。它基于 emotion,提供了标准化的 sx 属性和主题结构,适合需要统一视觉规范、减少样式决策疲劳的中大型团队协作项目。

  • rebass:

    不建议在新项目中选择 rebass。该库已 officially 停止维护(Archived),不再接收功能更新或安全修复。如果你正在维护旧项目,建议制定迁移计划,转向 theme-ui 或其他现代组件系统,以避免技术债务和潜在的安全风险。

styled-components的README


Fast, expressive styling for React.
Server components, client components, streaming SSR, React Native—one API.

npm downloads gzip size

styled-components is largely maintained by one person. Please help fund the project for consistent long-term support and updates: Open Collective


Style React components with real CSS, scoped automatically and delivered only when needed. No class name juggling, no separate files, no build step required.

  • Works everywhere React runs. Server components, client components, streaming SSR, and React Native—same API, automatic runtime detection.
  • Full CSS, no compromises. Media queries, pseudo-selectors, nesting, keyframes, global styles. If CSS supports it, so does styled-components.
  • TypeScript-first. Built-in types ship with the package. Props flow through to your styles with full inference—no @types install, no manual generics.
  • <13kB gzipped. Small enough to disappear in your bundle. No build plugin required.

Install

npm install styled-components
pnpm / yarn
pnpm add styled-components
yarn add styled-components

Quick examples

Dynamic props

Vary styles based on component props. Prefix transient props with $ to keep them off the DOM.

import styled from 'styled-components';

const Button = styled.button<{ $primary?: boolean }>`
  background: ${props => (props.$primary ? 'palevioletred' : 'white')};
  color: ${props => (props.$primary ? 'white' : 'palevioletred')};
  font-size: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

<Button>Normal</Button>
<Button $primary>Primary</Button>

Extending styles

Build variants on top of existing styled components.

const TomatoButton = styled(Button)`
  background: tomato;
  color: white;
  border-color: tomato;
`;

Polymorphic as prop

Swap the rendered element without changing styles.

// Renders a <a> tag with Button styles
<Button as="a" href="/home">Link Button</Button>

Pseudos and nesting

Use & to reference the component's generated class name—works with pseudo-classes, pseudo-elements, and nested selectors.

const Input = styled.input`
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 0.5em;

  &:focus {
    border-color: palevioletred;
    outline: none;
  }

  &::placeholder {
    color: #aaa;
  }
`;

Animations

Define @keyframes once, reference them across components. Names are scoped automatically.

import styled, { keyframes } from 'styled-components';

const rotate = keyframes`
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
`;

const Spinner = styled.div`
  animation: ${rotate} 1s linear infinite;
  width: 40px;
  height: 40px;
  border: 3px solid palevioletred;
  border-top-color: transparent;
  border-radius: 50%;
`;

Theming

Share design tokens across your app via React context. Every styled component receives props.theme.

import styled, { ThemeProvider } from 'styled-components';

const theme = {
  fg: 'palevioletred',
  bg: 'white',
};

const Card = styled.div`
  background: ${props => props.theme.bg};
  color: ${props => props.theme.fg};
  padding: 2em;
`;

<ThemeProvider theme={theme}>
  <Card>Themed content</Card>
</ThemeProvider>

RSC-compatible themes

createTheme turns your tokens into CSS custom properties. Class name hashes stay stable across theme variants—no hydration mismatch when switching light/dark.

import styled, { createTheme, ThemeProvider } from 'styled-components';

const { theme, GlobalStyle: ThemeVars } = createTheme({
  colors: {
    fg: 'palevioletred',
    bg: 'white',
  },
  space: {
    md: '1rem',
  },
});

const Card = styled.div`
  color: ${theme.colors.fg};       /* var(--sc-colors-fg, palevioletred) */
  background: ${theme.colors.bg};
  padding: ${theme.space.md};
`;

// Render <ThemeVars /> at the root to emit the CSS variable declarations
// Pass the theme to ThemeProvider for stable hashes
<ThemeProvider theme={theme}>
  <ThemeVars />
  <Card>Token-driven content</Card>
</ThemeProvider>

Shared styles with css

Extract reusable style blocks to share across components or apply conditionally.

import styled, { css } from 'styled-components';

const truncate = css`
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
`;

const Label = styled.span`
  ${truncate}
  max-width: 200px;
`;

Styling third-party components

Wrap any component that accepts a className prop.

import styled from 'styled-components';
import { Link } from 'react-router-dom';

const StyledLink = styled(Link)`
  color: palevioletred;
  text-decoration: none;

  &:hover {
    text-decoration: underline;
  }
`;

Global styles

Inject app-wide CSS like resets and font faces. Supports theming and dynamic updates.

import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  body {
    margin: 0;
    font-family: system-ui, sans-serif;
  }
`;

// Render <GlobalStyle /> at the root of your app

Attrs

Set default or static HTML attributes so consumers don't have to.

const PasswordInput = styled.input.attrs({
  type: 'password',
  placeholder: 'Enter password',
})`
  border: 1px solid #ccc;
  padding: 0.5em;
`;

Documentation

Community

Contributing guidelines | Code of Conduct | awesome-styled-components

Contributors

This project exists thanks to all the people who contribute.

Backers

Thank you to all our backers! [Become a backer]

Sponsors

Support this project by becoming a sponsor. [Become a sponsor]

Acknowledgements

This project builds on earlier work by Charlie Somerville, Nik Graf, Sunil Pai, Michael Chan, Andrey Popp, Jed Watson, and Andrey Sitnik. Special thanks to @okonet for the logo.

License

Licensed under the MIT License, Copyright © 2016-present styled-components contributors. See LICENSE for details.