Emotion logo

Emotion

React 生态最成熟的高灵活度 CSS-in-JS 库之一

精选工具CSS-in-JSReactstyledcss-prop

Emotion 提供 styled 组件和 css prop 两种核心写法,兼顾组件化样式和灵活表达,是 MUI 默认样式引擎的重要基础。

Emotion

高性能、灵活的 CSS-in-JS 库,提供 styled 组件 API 和独特的 css prop 两种主流样式编写方式,是 MUI 等主流组件库的默认样式引擎。

技术简介说明

Emotion 是一个为 JavaScript 而生的 CSS-in-JS 样式库,它允许开发者使用 JavaScript 编写样式,同时保留完整 CSS 能力——包括嵌套选择器、伪类、媒体查询、动画关键帧等。Emotion 的核心设计哲学是在运行时(runtime)通过 JavaScript 动态生成作用域化的 CSS 类名并注入页面,兼具 styled-components 的组件化思路和更灵活的 css prop 直接样式能力。

Emotion 采用 monorepo 架构,提供多个独立包:@emotion/react(React 集成,含 css prop 和 ThemeProvider)、@emotion/styled(styled 组件 API)、@emotion/css(框架无关的 vanilla 用法)等。它支持字符串模板和对象两种样式语法,自动处理浏览器前缀、样式去重和源码映射,配合 Babel 插件可实现样式压缩和类名优化。

截至 2025-2026 年,Emotion 仍是 npm 上周下载量最高的 CSS-in-JS 库(@emotion/react 约 1900 万次/周),主要驱动力来自 MUI(Material UI)v5/v6 的深度集成。但受 React Server Components(RSC)不兼容、Next.js App Router 集成复杂等现代 React 架构变化影响,Emotion 和整个运行时 CSS-in-JS 阵营正面临向零运行时方案(vanilla-extract、Panda CSS、CSS Modules 等)迁移的行业趋势压力。

基本信息

项目详情
官网https://emotion.sh
GitHubhttps://github.com/emotion-js/emotion
LicenseMIT
最新版本@emotion/react v11.14.0(2024.12);@emotion/styled v11.14.1(2025.06);@emotion/jest v11.14.2(2025.11)
GitHub Stars18k+
npm 周下载量@emotion/react ~19M;@emotion/styled ~12.8M(2025 年中)
维护者Emotion 团队(mitchellhamilton、tkh44、Andarist 等核心贡献者),社区驱动,Open Collective 赞助
包管理Monorepo(pnpm + changesets)
首次发布2017 年
React 19 兼容性v11 未完全兼容,需 --legacy-peer-deps;完整 React 19 支持计划在 v12

快速上手

安装

# React 项目基础(css prop + ThemeProvider)
npm install @emotion/react
 
# 加上 styled 组件 API
npm install @emotion/react @emotion/styled
 
# 框架无关用法(非 React 项目)
npm install @emotion/css
 
# 开发依赖:Babel 插件(推荐,用于样式优化和源码映射)
npm install -D @emotion/babel-plugin

基础配置

Babel 配置(推荐)

// .babelrc
{
  "plugins": [
    "@emotion"
    // ⚠️ @emotion/babel-plugin 必须放在 plugins 数组的第一位
  ]
}

TypeScript 配置

// tsconfig.json
{
  "compilerOptions": {
    "jsxImportSource": "@emotion/react"
    // 启用 css prop 支持
  }
}

主题类型声明

// types/emotion.d.ts
import '@emotion/react'
 
declare module '@emotion/react' {
  export interface Theme {
    colors: {
      primary: string
      secondary: string
      background: string
      text: string
    }
    spacing: {
      sm: string
      md: string
      lg: string
    }
    breakpoints: {
      mobile: string
      tablet: string
      desktop: string
    }
  }
}

最小示例

方式一:css prop(推荐用于轻量样式)

import { css } from '@emotion/react'
 
function App() {
  return (
    <div
      css={css`
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 24px;
        background-color: #f5f5f5;
      `}
    >
      <h1
        css={{
          color: '#333',
          fontSize: '2rem',
          marginBottom: '16px',
        }}
      >
        Hello Emotion
      </h1>
      <p
        css={(theme) => css`
          color: ${theme.colors.text};
          font-size: 1rem;
        `}
      >
        使用 css prop 直接编写样式
      </p>
    </div>
  )
}

方式二:styled API(推荐用于可复用组件)

import styled from '@emotion/styled'
 
const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px;
  background-color: ${(props) => props.theme.colors.background};
`
 
const Title = styled.h1({
  color: '#333',
  fontSize: '2rem',
  marginBottom: '16px',
})
 
// 支持动态 props
const Button = styled.button<{ variant: 'primary' | 'secondary' }>`
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  background-color: ${(props) =>
    props.variant === 'primary' ? '#007bff' : '#6c757d'};
  color: white;
 
  &:hover {
    opacity: 0.9;
  }
`
 
function App() {
  return (
    <Container>
      <Title>Hello Emotion</Title>
      <Button variant="primary">点击我</Button>
    </Container>
  )
}

方式三:完整应用(含主题)

import React from 'react'
import { ThemeProvider } from '@emotion/react'
import styled from '@emotion/styled'
 
const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    background: '#ffffff',
    text: '#333333',
  },
  spacing: {
    sm: '8px',
    md: '16px',
    lg: '24px',
  },
  breakpoints: {
    mobile: '480px',
    tablet: '768px',
    desktop: '1024px',
  },
}
 
const Card = styled.div`
  background: ${(props) => props.theme.colors.background};
  color: ${(props) => props.theme.colors.text};
  padding: ${(props) => props.theme.spacing.lg};
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
 
  @media (max-width: ${(props) => props.theme.breakpoints.tablet}) {
    padding: ${(props) => props.theme.spacing.md};
  }
`
 
function App() {
  return (
    <ThemeProvider theme={theme}>
      <Card>
        <h2>主题化卡片</h2>
        <p>样式从 theme 对象中读取设计令牌</p>
      </Card>
    </ThemeProvider>
  )
}
 
export default App

核心概念与架构

运行时样式注入机制

Emotion 是一个运行时 CSS-in-JS 库,其核心工作流程为:

JavaScript 样式定义
    ↓
Babel 插件编译优化(可选)
    ↓
运行时序列化 + 哈希生成类名
    ↓
Stylis 预处理(自动前缀、嵌套展平)
    ↓
<style> 标签注入 <head>
    ↓
CSS 类名绑定到 DOM 元素
  1. 样式序列化(Serialization):将 JavaScript 对象或模板字符串转换为 CSS 字符串
  2. 哈希类名生成:基于样式内容生成唯一哈希类名(如 css-1a2b3c),确保样式作用域隔离
  3. Stylis 预处理:内置 Stylis 预处理器,自动添加浏览器前缀、展平嵌套规则
  4. 样式注入:通过 &lt;style&gt; 标签动态注入到 &lt;head&gt; 中,使用 insertRule API 提升性能
  5. 缓存与去重:已注入的样式规则会被缓存,避免重复注入

包架构(Monorepo)

@emotion/
├── react          # React 集成核心(css prop、ThemeProvider、useTheme)
├── styled         # styled 组件 API
├── css            # 框架无关的 vanilla API
├── cache          # 样式缓存和 sheet 管理
├── serialize      # CSS 序列化器
├── sheet          # 样式表操作(insertRule / <style> 标签)
├── hash           # 高性能哈希函数
├── weak-memoize   # 弱引用记忆化
├── babel-plugin   # Babel 编译优化插件
├── jest           # Jest 测试序列化和匹配器
├── server         # SSR 样式提取
├── eslint-plugin  # ESLint 规则
├── native         # React Native 适配(实验性)
└── primitives     # 基础组件原语(实验性)

两种 API 范式

范式核心理念适用场景
css prop@emotion/react将样式作为 prop 直接附加到任意 JSX 元素一次性样式、快速原型、布局微调
styled API@emotion/styled创建带样式的可复用组件设计系统、组件库、需要语义化的场景

核心特性

1. 双 API 范式(css prop + styled)

Emotion 同时提供 css prop 和 styled API,开发者可根据场景自由选择。css prop 不需要包装组件,可直接在 JSX 元素上编写样式;styled API 则创建独立的带样式组件。两种 API 共享相同的样式能力,可以混合使用。

2. 字符串与对象双语法

支持模板字符串(类 CSS 语法)和 JavaScript 对象两种样式定义方式,均可使用嵌套、伪类、媒体查询等 CSS 特性:

// 模板字符串语法
const A = styled.div`
  color: red;
  &:hover { color: blue; }
  @media (min-width: 768px) { font-size: 1.2rem; }
`
 
// 对象语法(TypeScript 推断更友好)
const B = styled.div({
  color: 'red',
  '&:hover': { color: 'blue' },
  '@media (min-width: 768px)': { fontSize: '1.2rem' },
})

3. 主题系统(ThemeProvider)

内置 ThemeProvideruseTheme() hook 和 withTheme() HOC,通过 React Context 将主题对象注入组件树。支持嵌套 ThemeProvider 实现局部主题覆盖。

import { ThemeProvider, useTheme } from '@emotion/react'
 
function ThemedComponent() {
  const theme = useTheme()
  return <div css={{ color: theme.colors.primary }}>主题色文字</div>
}

4. 高性能运行时

  • 使用 insertRule API 批量注入样式,比逐个插入 &lt;style&gt; 标签更高效
  • 内置样式缓存和记忆化(weak-memoize),相同样式不重复计算
  • 配合 Babel 插件预编译,运行时开销大幅降低
  • 基准测试:1000 次动态渲染约 38ms(配合 Babel 插件),优于 styled-components 的 62ms

5. SSR 支持

提供完整的 @emotion/server 包,支持多种 SSR 策略:

  • renderStylesToString:内联关键 CSS 到 HTML 字符串
  • renderStylesToNodeStream:流式 SSR
  • extractCriticalToChunks + constructStyleTagsFromChunks:提取关键 CSS 集中注入 &lt;head&gt;

6. 源码映射(Source Maps)

开发环境下自动生成 CSS 源码映射,调试时可在 DevTools 中直接定位到原始 JS 文件中的样式定义位置。Babel 插件会增强源码映射精度。

7. 样式组合(Composition)

css 工具函数支持样式组合,可以将多个样式对象/类名合并,cx 函数(来自 @emotion/css)用于条件性合并类名:

import { css, cx } from '@emotion/css'
 
const base = css`
  padding: 8px;
  border-radius: 4px;
`
const primary = css`
  background: #007bff;
  color: white;
`
const large = css`
  padding: 16px;
  font-size: 1.2rem;
`
 
function Badge({ isLarge }: { isLarge: boolean }) {
  return <span className={cx(base, primary, isLarge && large)}>标签</span>
}

8. TypeScript 深度集成

通过模块声明(module augmentation)为 Theme 接口添加自定义类型,使 props.theme 在所有 styled 组件和 css prop 中获得完整的类型推断和自动补全。

9. Babel 插件优化

@emotion/babel-plugin 提供:

  • 样式压缩和类名优化
  • 组件名自动注入到生成类名(如 css-abc123-Button
  • 源码映射增强
  • 可选的样式提取(extract mode)为静态 CSS 文件
  • sourceMap 支持

10. Jest 测试工具

@emotion/jest 提供 Jest 序列化器和自定义匹配器,使快照测试输出可读的 CSS 样式而非哈希类名:

import serializer from '@emotion/jest/serializer'
 
expect.addSnapshotSerializer(serializer)
 
test('按钮样式', () => {
  const { container } = render(<Button>Click</Button>)
  expect(container.firstChild).toMatchEmotionSnapshot()
})

生态图

Emotion 生态
│
├── 核心包
│   ├── @emotion/react          # React 核心(css prop、ThemeProvider、useTheme)
│   ├── @emotion/styled         # styled 组件 API
│   ├── @emotion/css            # 框架无关的 vanilla CSS-in-JS
│   └── @emotion/cache          # 样式缓存配置
│
├── 工具链
│   ├── @emotion/babel-plugin   # Babel 编译优化
│   ├── @emotion/server         # SSR 样式提取
│   ├── @emotion/jest           # Jest 测试序列化器
│   └── @emotion/eslint-plugin  # ESLint 规则
│
├── 底层能力
│   ├── @emotion/serialize      # CSS 序列化
│   ├── @emotion/sheet          # 样式表管理
│   ├── @emotion/hash           # 哈希生成
│   └── @emotion/weak-memoize   # 记忆化工具
│
├── 第三方集成
│   ├── MUI (Material UI)       # 默认样式引擎(v5/v6)
│   ├── Chakra UI               # 部分版本使用 Emotion
│   ├── Theme UI                # 基于 Emotion 的主题化 UI
│   ├── tss-react               # Emotion + TypeScript 样式方案
│   ├── @xstyled/emotion        # xstyled 的 Emotion 适配
│   └── gatsby-plugin-emotion   # Gatsby 官方集成插件
│
├── React Native(实验性)
│   └── @emotion/native         # React Native 样式适配
│
└── 框架集成
    ├── Next.js                 # Pages Router 良好支持;App Router 需要额外配置
    ├── Gatsby                  # 官方插件 gatsby-plugin-emotion
    ├── Remix                   # 客户端渲染可用
    └── Create React App        # 通过 Babel Macros 支持

适用场景

1. MUI 组件库项目

MUI v5/v6 将 Emotion 作为默认样式引擎。使用 MUI 意味着已经间接依赖 Emotion,可以直接利用 Emotion 的 styled API 和主题系统扩展 MUI 组件样式,无需额外引入样式库。

2. 设计系统与组件库开发

Emotion 的主题系统、样式组合能力和 TypeScript 类型安全使其适合构建设计系统。通过 ThemeProvider 和模块化的主题令牌,可以实现一致的视觉语言和灵活的定制能力。

3. 需要动态样式的应用

运行时 CSS-in-JS 天然擅长处理动态、依赖 props 或状态的样式。Emotion 的 styled 组件可以直接根据 props 计算样式值,无需预定义 CSS 类或使用 CSS 变量 hack。

4. 从 styled-components 迁移的项目

Emotion 的 API 与 styled-components 高度相似(模板字符串 + props 函数),迁移成本较低。特别是 styled-components 进入维护模式后,Emotion 成为同类运行时方案中最成熟的替代选择。

5. 客户端渲染(CSR)应用

Emotion 在纯客户端渲染场景中表现最佳——无 SSR 复杂性、无 RSC 兼容性问题。SPA(单页应用)、后台管理系统、仪表盘等 CSR 场景非常适合使用 Emotion。

6. 快速原型和迭代

css prop 允许直接在 JSX 元素上编写样式而无需创建单独的 styled 组件,非常适合快速原型开发和频繁迭代的设计阶段。

7. 需要样式测试的项目

@emotion/jest 提供快照测试序列化器,使测试输出中包含可读的 CSS 规则而非哈希类名,提升样式测试的可维护性。

开发与工程化

项目结构建议

src/
├── theme/
│   ├── index.ts           # 主题对象导出
│   ├── tokens.ts          # 设计令牌(颜色、间距、字体等)
│   └── types.ts           # 主题 TypeScript 类型
├── components/
│   ├── Button/
│   │   ├── index.ts
│   │   ├── Button.tsx     # 组件逻辑
│   │   └── styles.ts      # Emotion 样式(可选拆分)
│   └── ...
├── styles/
│   ├── global.ts          # 全局样式
│   └── mixins.ts          # 样式 mixins / 复用片段
└── App.tsx

全局样式

import { Global, css } from '@emotion/react'
 
function GlobalStyles() {
  return (
    <Global
      styles={css`
        *,
        *::before,
        *::after {
          box-sizing: border-box;
          margin: 0;
          padding: 0;
        }
 
        body {
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
            sans-serif;
          -webkit-font-smoothing: antialiased;
        }
      `}
    />
  )
}

动画定义

import { keyframes } from '@emotion/react'
import styled from '@emotion/styled'
 
const fadeIn = keyframes`
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
`
 
const AnimatedCard = styled.div`
  animation: ${fadeIn} 0.3s ease-out;
`

Babel 插件配置(完整选项)

// .babelrc
{
  "plugins": [
    [
      "@emotion",
      {
        "sourceMap": true,           // 开发环境开启源码映射
        "autoLabel": "dev-only",     // 开发环境自动添加标签
        "labelFormat": "[local]",    // 类名格式:css-hash-ComponentName
        "importMap": {               // 自定义导入映射
          "@emotion/styled": {
            "styled": {
              "canonicalImport": ["@emotion/styled", "default"],
              "computedStyles": ["Button"]
            }
          }
        }
      }
    ]
  ]
}

与 Next.js 集成

// pages/_app.tsx(Pages Router)
import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'
import { ThemeProvider } from '@emotion/react'
import theme from '@/theme'
 
const clientSideEmotionCache = createCache({ key: 'css' })
 
function MyApp({ Component, pageProps }) {
  return (
    <CacheProvider value={clientSideEmotionCache}>
      <ThemeProvider theme={theme}>
        <Component {...pageProps} />
      </ThemeProvider>
    </CacheProvider>
  )
}
// pages/_document.tsx(Pages Router SSR)
import { Html, Head, Main, NextScript } from 'next/document'
import createCache from '@emotion/cache'
import { renderStylesToString } from '@emotion/server'
 
export default function Document({ html, styles }) {
  return (
    <Html lang="zh-CN">
      <Head />
      <body dangerouslySetInnerHTML={{ __html: html }}>
        {styles}
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}
 
Document.getInitialProps = async (ctx) => {
  const cache = createCache({ key: 'css' })
  const extractStyles = renderStylesToString
  // ... 自定义 SSR 逻辑
}

注意:Next.js App Router 下 Emotion 兼容性不佳,存在 Turbopack hydration 错误等问题。建议使用 Pages Router 或考虑迁移至 CSS Modules / Tailwind CSS。

Transient Props(瞬态属性)

Emotion styled 组件默认会将所有 props 转发到 DOM,自定义非标准属性会产生 React 警告。使用 shouldForwardProp 或 transient props($ 前缀)避免:

// 方式一:shouldForwardProp
const Box = styled('div', {
  shouldForwardProp: (prop) => prop !== 'isHighlighted',
})<{ isHighlighted: boolean }>`
  background: ${(props) => (props.isHighlighted ? 'yellow' : 'white')};
`
 
// 方式二:transient props($ 前缀,Emotion 11+)
const Box = styled.div<{ $isHighlighted: boolean }>`
  background: ${(props) => (props.$isHighlighted ? 'yellow' : 'white')};
`
// $ 前缀的 props 自动不会转发到 DOM

性能与安全

性能特征

指标表现说明
运行时包大小@emotion/react ~7KB gzip比 styled-components(~12KB)更轻量
运行时渲染~38ms / 1000 次动态渲染(配 Babel 插件)优于 styled-components 的 ~62ms
样式注入方式insertRule API比插入 &lt;style&gt; 标签更快
缓存机制weak-memoize相同样式不重复计算
首屏影响运行时计算开销存在 JS 执行 + CSS 注入延迟
Tree-shaking部分支持按包独立引入可减小体积

性能优化建议

  1. 始终使用 @emotion/babel-plugin:预编译可显著减少运行时开销
  2. 避免在渲染路径中创建样式对象:将 css 调用提到组件外部或使用 useMemo
  3. 使用 memo 包裹 styled 组件:防止不必要的重渲染
  4. 减少动态样式变化:频繁变化的 props 会导致重复样式计算和注入
  5. SSR 时提取关键 CSS:使用 extractCriticalToChunks 避免 FOUC

安全考量

  1. XSS 风险:Emotion 样式完全由 JavaScript 控制,如果样式值中包含用户输入(如 userInput 作为 CSS 值),需确保输入已清洗,避免 expression()url() 等 CSS 注入向量
  2. CSP 兼容性:运行时注入 &lt;style&gt; 标签需要 CSP 策略允许 unsafe-inline 或使用 nonce。可通过 @emotion/cache 配置 nonce:
    const cache = createCache({
      key: 'css',
      nonce: 'your-nonce-value',
    })
  3. CSS 注入攻击:避免将不可信数据直接作为 css 值或模板字符串插值

技术对比

与同类方案对比

特性Emotionstyled-componentsTailwind CSSCSS Modulesvanilla-extractPanda CSS
类型运行时 CSS-in-JS运行时 CSS-in-JS原子化 Utility编译时作用域 CSS编译时 CSS-in-TS编译时 + 运行时
包大小~7KB gzip~12KB gzip~0KB(纯 CSS)0KB0KB(编译时提取)~0KB
API 风格css prop + styledstyledUtility classes.module.css.css.ts 文件食谱(recipes)+ cva
TypeScript✅ 优秀(模块增强)✅ 良好✅ 支持⚠️ 需额外配置✅ 一流支持✅ 一流支持
React 19 / RSC❌ 不兼容❌ 不兼容✅ 完全兼容✅ 完全兼容✅ 完全兼容✅ 完全兼容
Next.js App Router⚠️ 复杂配置⚠️ 复杂配置✅ 原生支持✅ 原生支持✅ 原生支持✅ 原生支持
主题系统✅ 内置 ThemeProvider✅ 内置 ThemeProvider⚠️ 需 Tailwind 配置❌ 手动管理✅ 主题 API✅ 内置
SSR✅ 完整支持✅ 完整支持✅ 零配置✅ 零配置✅ 零配置✅ 零配置
动态样式✅ 天然支持✅ 天然支持⚠️ 需 CSS 变量⚠️ 有限⚠️ 需 CSS 变量✅ 支持
运行时开销有(insertRule)有(insertRule)极低
学习曲线中高中高
npm 周下载量~19M~10.9M~20M+N/A(Next.js 内置)~1M~0.3M
社区趋势稳定/微降维护模式强劲增长稳定增长中增长中
最佳搭档MUI独立项目任何框架任何框架TypeScript 项目设计系统

Emotion vs styled-components

维度Emotionstyled-components
css prop✅ 独有,灵活直接❌ 不支持
API 灵活性双 API(css prop + styled)单 API(styled only)
包大小~7KB gzip(更小)~12KB gzip
运行时性能~38ms / 1000 次渲染~62ms / 1000 次渲染
对象语法✅ 原生支持⚠️ v6 开始支持
项目状态持续维护,计划 v12已进入维护模式(2024)
生态绑定MUI(巨大存量)独立社区

最佳实践

1. 始终使用 Babel 插件

npm install -D @emotion/babel-plugin

在构建流程中加入 @emotion/babel-plugin,可获得:类名可读化(调试友好)、样式压缩、自动源码映射、运行时性能提升。

2. 设计令牌集中管理

// theme/tokens.ts
export const tokens = {
  colors: {
    primary: { 50: '#eff6ff', 500: '#3b82f6', 900: '#1e3a8a' },
    neutral: { 50: '#fafafa', 500: '#6b7280', 900: '#111827' },
  },
  spacing: { 0: '0', 1: '4px', 2: '8px', 3: '12px', 4: '16px', 6: '24px', 8: '32px' },
  fontSizes: { xs: '12px', sm: '14px', base: '16px', lg: '18px', xl: '20px' },
  radii: { sm: '4px', md: '8px', lg: '12px', full: '9999px' },
} as const

3. 样式复用使用 css 工具函数

import { css } from '@emotion/react'
 
const flexCenter = css`
  display: flex;
  align-items: center;
  justify-content: center;
`
 
const cardBase = css`
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  padding: 16px;
`
 
// 组合使用
const CenteredCard = styled.div`
  ${flexCenter};
  ${cardBase};
`

4. 避免渲染路径中的样式对象创建

// ❌ 不好:每次渲染都创建新对象
function Bad() {
  return <div css={{ color: 'red', padding: '16px' }}>内容</div>
}
 
// ✅ 好:提取到组件外部或使用变量
const redStyle = css`
  color: red;
  padding: 16px;
`
function Good() {
  return <div css={redStyle}>内容</div>
}

5. 为 styled 组件使用有意义的类名

配合 Babel 插件的 labelFormat 选项,使生成的类名包含组件名:

// .babelrc
{
  "plugins": [
    ["@emotion", { "labelFormat": "[filename]--[local]" }]
  ]
}
// 生成类名如:css-abc123-Button--Container

6. SSR 场景使用 CacheProvider

import createCache from '@emotion/cache'
import { CacheProvider } from '@emotion/react'
 
// 服务端每次请求创建独立 cache,避免样式泄漏
const cache = createCache({ key: 'my-app' })
 
function App({ children }) {
  return <CacheProvider value={cache}>{children}</CacheProvider>
}

7. 条件样式使用函数式 css prop

// ✅ 利用 theme 参数避免重复 useTheme
<p css={(theme) => css`color: ${theme.colors.primary};`}>
  主题色文字
</p>

技术局限与边界

1. React Server Components(RSC)不兼容

这是 Emotion 在 2025-2026 年面临的最核心局限。 Emotion 依赖运行时 JavaScript 在浏览器中注入样式,而 RSC 在服务端渲染且零 JS 发送到客户端,两者架构根本不兼容。唯一解法是在使用 Emotion 的组件上添加 'use client',但这会完全抵消 RSC 的性能优势。这不是版本更新能解决的问题,是架构层面的矛盾。

2. Next.js App Router 集成复杂

在 Next.js 15 的 App Router 下,Emotion 存在已知问题:

  • Turbopack 下的 hydration 错误
  • 需要手动配置 CacheProvider 和样式注册表
  • 官方(Vercel)推荐 Tailwind CSS 或 CSS Modules,未提供 Emotion 的 App Router 示例

3. 运行时性能开销

尽管在 CSS-in-JS 中属于较优,但与零运行时方案相比,Emotion 仍存在:

  • 首屏 JS 执行开销(样式序列化 + 注入)
  • 动态样式变化时的重复计算
  • 阻塞渲染的 &lt;style&gt; 标签注入

4. CSP(内容安全策略)限制

运行时注入 &lt;style&gt; 标签需要 CSP 允许 unsafe-inline 或配置 nonce,在安全要求严格的企业环境中可能成为障碍。

5. React DevTools 干扰

Emotion 运行时会插入内部组件(如 GlobalThemeProvider),使 React DevTools 组件树变得杂乱,影响调试体验。

6. 社区发展方向不确定

  • styled-components 已进入维护模式,CSS-in-JS 阵营整体面临信心危机
  • MUI 正在开发零运行时方案(Pigment CSS),计划未来替代 Emotion
  • 新项目普遍推荐 Tailwind CSS / CSS Modules / vanilla-extract 等零运行时方案
  • Emotion 的长期发展方向取决于 v12(React 19 兼容版本)能否及时推出

7. 包体积与性能权衡

虽然 @emotion/react 的 gzip 大小(~7KB)已经较小,但对比 Tailwind CSS(运行时 0KB)或 CSS Modules(编译时 0KB),仍有明确的性能差距。

学习资源

官方文档

教程与博客

对比与趋势

社区

2026 年现状

市场地位

Emotion 仍是 npm 上周下载量最高的 CSS-in-JS 库(@emotion/react 约 1900 万次/周),但这主要得益于 MUI 的巨大存量用户。作为独立选型,新项目选择 Emotion 的比例正在下降。

React 19 / Server Components 困境

Emotion v11 明确不兼容 React 19,完整的 React 19 支持被推迟到 v12。在 RSC 架构下,Emotion 的运行时注入模型与零 JS 客户端的服务端渲染理念根本冲突。要在 App Router 中使用,必须为所有使用 Emotion 的组件添加 'use client',这实质上放弃了 RSC 的所有优势(更小的 JS bundle、更快的首次加载、服务端数据访问)。

行业趋势

  • styled-components 已于 2024 年进入维护模式,不再积极开发新功能
  • MUI 正在开发 Pigment CSS(零运行时方案),计划在未来的 v7+ 版本中替代 Emotion
  • Next.js 官方推荐 Tailwind CSS 和 CSS Modules,CSS-in-JS 在 App Router 下被视为二等公民
  • 社区共识 正在转向零运行时方案:vanilla-extract、Panda CSS、StyleX、CSS Modules

选型建议

场景建议
已有 MUI + Emotion 存量项目继续使用,暂无紧迫迁移必要;关注 MUI v7 的 Pigment CSS 进展
从 styled-components 迁移Emotion 可作为过渡方案(API 相似、迁移成本低),但建议直接评估零运行时方案
Next.js App Router 新项目❌ 不推荐 Emotion,优先选择 Tailwind CSS 或 CSS Modules
纯 CSR / SPA 应用✅ 可以使用,Emotion 在此场景下体验良好
设计系统 / 组件库⚠️ 可用但需谨慎,考虑零运行时方案作为长期策略
快速原型 / 内部工具✅ 适合,css prop 的灵活性在原型开发中优势明显

未来展望

Emotion 短期内(2026 年)不会消亡——MUI 生态的巨大存量和 CSS-in-JS 的运行时灵活性仍然有其不可替代的价值。但长期来看,随着 React 生态向 Server Components 和零运行时方向演进,Emotion 的核心运行时模型正面临架构层面的挑战。v12 能否及时解决 React 19 兼容问题、以及是否会探索零运行时方向,将决定 Emotion 的长期命运。