Linaria logo

Linaria

在构建阶段提取真实 CSS 文件的零运行时 CSS-in-JS 库

精选工具CSS-in-JS零运行时编译时提取React

Linaria 允许开发者在 JavaScript 和 TypeScript 中编写样式,并在编译时输出独立 CSS 文件,是传统运行时 CSS-in-JS 的重要替代方案。

Linaria

零运行时 CSS-in-JS 库——在构建时将样式提取为真实 CSS 文件,运行时零开销。

技术简介说明

Linaria 是由 Callstack 团队开源的 零运行时 CSS-in-JS 库。它的核心理念是:开发者在 JavaScript 中编写样式,但在构建阶段(build time),所有 CSS 会被提取为独立的 .css 文件,最终产物中不包含任何样式计算相关的运行时 JavaScript 代码。这与 styled-components、Emotion 等传统 CSS-in-JS 方案在浏览器中动态生成 <style> 标签的做法截然不同。

Linaria 的技术基础是 wyw-in-js(What You Write in JS)编译引擎。它在 Babel 编译阶段对 css / styled 等 tagged template literal 进行静态分析与求值,将合法的 CSS 规则提取到独立的样式文件中,仅在需要动态样式时通过 CSS 变量(Custom Properties)在运行时传递值,避免了 JavaScript 到 CSS 的运行时转换开销。

自 2018 年首次发布以来,Linaria 持续演进。截至 2026 年 7 月,Linaria 已发布 v8.0.0,要求 Node.js >= 22.12.0,底层切换至 wyw-in-js v2,采用混合求值策略(hybrid evaluation strategy),在保持零运行时特性的同时大幅提升了构建稳定性和兼容性。


基本信息

项目信息
官方网站https://linaria.dev/
GitHub 仓库https://github.com/callstack/linaria
LicenseMIT
最新版本v8.0.0(2026-06-14 发布)
主要维护者Callstack(核心贡献者:Paweł Trysła、Satyajit Sahoo、Michał Pierzchała、Anton Evzhakov 等,累计 90+ 贡献者)
GitHub Stars~12.3k
语言TypeScript 87.2%、JavaScript 12.4%
包管理器npm / pnpm / yarn
底层引擎wyw-in-js v2

快速上手

安装

# npm
npm install @linaria/core @linaria/react @wyw-in-js/babel-preset
 
# pnpm
pnpm add @linaria/core @linaria/react @wyw-in-js/babel-preset
 
# yarn
yarn add @linaria/core @linaria/react @wyw-in-js/babel-preset

Babel 配置

.babelrcbabel.config.js 中添加 @wyw-in-js/babel-preset

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@wyw-in-js/babel-preset"
  ]
}

webpack 配置

Linaria v8 基于 wyw-in-js,webpack 集成通过 @wyw-in-js/webpack-loader 完成:

npm install @wyw-in-js/webpack-loader --save-dev
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.[jt]sx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env',
                '@babel/preset-react',
                '@wyw-in-js/babel-preset',
              ],
            },
          },
        ],
      },
    ],
  },
};

Vite 配置

npm install @wyw-in-js/vite --save-dev
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import wyw from '@wyw-in-js/vite';
 
export default defineConfig({
  plugins: [
    wyw({
      include: ['**/*.{ts,tsx}'],
      babelOptions: {
        presets: ['@babel/preset-env', '@babel/preset-react'],
      },
    }),
    react(),
  ],
});

最小示例

使用 css 标签

import { css } from '@linaria/core';
 
// 样式会在构建时被提取为真实 CSS 文件
const header = css`
  text-transform: uppercase;
  font-family: 'Georgia', serif;
  font-size: 24px;
  color: #333;
 
  &:hover {
    color: #007bff;
  }
 
  @media (max-width: 768px) {
    font-size: 18px;
  }
`;
 
function Header() {
  return <h1 className={header}>Hello Linaria</h1>;
}

使用 styled 标签

import { styled } from '@linaria/react';
 
const Title = styled.h1`
  font-family: 'Georgia', serif;
  font-size: 24px;
  color: #333;
`;
 
// 动态样式通过 CSS 变量实现,无需运行时
const Container = styled.div`
  font-size: 16px;
  color: ${props => props.color};
  border: 1px solid red;
 
  &:hover {
    border-color: blue;
  }
 
  ${Title} {
    margin-bottom: 24px;
  }
`;
 
function App() {
  return (
    <Container color="purple">
      <Title>Hello Linaria</Title>
      <p>Zero runtime CSS-in-JS</p>
    </Container>
  );
}

核心概念与架构

编译时提取 CSS

Linaria 的核心机制是在 编译阶段 完成所有 CSS 的生成与提取:

  1. Babel 解析@wyw-in-js/babel-preset 在 Babel 编译阶段扫描源码中的 css / styled tagged template literal。
  2. 静态求值:wyw-in-js 引擎对模板中的 JS 表达式进行静态分析,尝试在构建时计算出最终值(如导入的变量、纯函数调用结果等)。
  3. CSS 提取:解析后的 CSS 规则被写入独立的 .css 文件,由 bundler(webpack / Vite / Rollup 等)处理并输出。
  4. 类名替换:原始 JS 中的 tagged template 被替换为提取后的 CSS 类名引用(字符串常量)。
  5. 动态值处理:对于无法在构建时求值的动态表达式(如 props => props.color),Linaria 通过 CSS 变量(Custom Properties)在运行时传递值,样式规则本身仍然是静态 CSS。

零运行时原理

"零运行时"意味着最终产物中 不包含任何用于样式计算的 JavaScript 代码。与 styled-components 等方案的区别:

维度传统 CSS-in-JS(运行时)Linaria(零运行时)
样式生成时机浏览器运行时构建时
产物形态JS + 动态 &lt;style&gt; 标签JS + 独立 .css 文件
运行时开销有(序列化、哈希、注入)无(仅 CSS 变量赋值)
CSS 缓存不可独立缓存浏览器原生缓存 CSS 文件
动态样式JS 运行时计算CSS 变量 + 内联 style

wyw-in-js 引擎

自 v5 起,Linaria 将核心编译能力抽象为独立的 wyw-in-js 项目,使其他库也能复用其"构建时求值 + 提取"的能力。Linaria v8 底层使用 wyw-in-js v2,默认采用 混合求值策略eval.strategy: 'hybrid'):先尝试静态分析,不可行时再回退到安全求值器。


核心特性

1. 零运行时开销

所有 CSS 在构建阶段提取为独立文件,运行时不执行任何样式计算逻辑,不向产物中注入多余的 JS 代码。

2. 熟悉的 tagged template literal API

与 styled-components / Emotion 共享相同的 cssstyled 语法,学习成本极低,迁移平滑。

3. 动态样式(基于 CSS 变量)

styled 组件中支持 props => props.color 形式的动态样式,底层通过 CSS 自定义属性实现,无需运行时 JS 计算:

const Box = styled.div`
  background-color: ${props => props.bg};
  width: ${props => props.size}px;
`;
 
// 编译后等价于:
// .Box { background-color: var(--bg); width: var(--size); }
// 运行时仅设置 CSS 变量值

4. Sass-like 嵌套语法

支持标准的 CSS 嵌套语法,包括 & 父选择器引用、媒体查询嵌套等:

const Card = styled.div`
  padding: 16px;
  border-radius: 8px;
 
  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  }
 
  & > h2 {
    margin-bottom: 8px;
  }
 
  @media (min-width: 768px) {
    padding: 24px;
  }
`;

5. CSS Source Maps

生成完整的 CSS Source Map,可以在浏览器 DevTools 中精确定位到样式定义的 JS 源文件和行号,调试体验与手写 CSS 无异。

6. Stylelint 集成

样式定义在 JS 中,但可以被 Stylelint 正常解析和检查,支持团队统一的 CSS 规范约束:

{
  "processors": ["@linaria/stylelint"]
}

7. 多框架支持

Linaria 核心不绑定 React:

  • @linaria/core — 框架无关的 css 标签
  • @linaria/react — React 专用的 styled 组件
  • 社区提供了 Vue、Svelte 等框架的集成

8. 原子样式支持

通过 @linaria/atomic 包支持原子化 CSS 生成,将样式拆分为最小粒度的原子类,最大化复用率。

9. JS 表达式求值

样式中可以直接使用 JavaScript 变量、函数、导入的模块等,无需 CSS 预处理器:

import { colors, spacing } from './design-tokens';
import { rem } from 'polished';
 
const Button = styled.button`
  background-color: ${colors.primary};
  padding: ${rem(spacing.md)};
  font-size: ${rem(16)};
`;

10. 多 Bundler 集成

通过 wyw-in-js 生态,支持 webpack、Vite、Rollup、esbuild、SvelteKit 等主流构建工具。


生态图

Linaria 生态
│
├── 核心包
│   ├── @linaria/core          # 框架无关的 css 标签
│   ├── @linaria/react         # React styled 组件
│   ├── @linaria/atomic        # 原子化 CSS 支持
│   └── @linaria/interop       # 与 styled-components/Emotion 选择器互操作
│
├── 编译引擎(wyw-in-js)
│   ├── @wyw-in-js/babel-preset    # Babel 预设
│   ├── @wyw-in-js/webpack-loader  # webpack 集成
│   ├── @wyw-in-js/vite            # Vite 集成
│   ├── @wyw-in-js/rollup          # Rollup 集成
│   ├── @wyw-in-js/esbuild         # esbuild 集成
│   └── @wyw-in-js/processor       # CSS 处理管线
│
├── 工具链集成
│   ├── @linaria/stylelint          # Stylelint 支持
│   ├── @linaria/shaker             # 静态分析求值器
│   ├── gatsby-plugin-linaria       # Gatsby 插件
│   └── craco-linaria               # CRA (create-react-app) 免 eject 集成
│
├── 配套生态
│   ├── polished                    # JS 样式工具函数(与 Linaria 配合使用)
│   └── design-tokens 集成           # 设计令牌在 JS 中的直接引用
│
└── 社区集成
    ├── linaria-vue                 # Vue 集成
    ├── linaria-svelte              # Svelte 集成
    └── linaria-next                # Next.js 集成方案

适用场景

1. 高性能 Web 应用

对首屏加载速度和运行时性能要求极高的应用(如电商、内容平台),零运行时方案可以避免 CSS-in-JS 的运行时开销。

2. 从 styled-components / Emotion 迁移

Linaria 的 API 与传统 CSS-in-JS 库高度一致,是迁移路径最短的方案,适合需要降低运行时开销但希望保留 CSS-in-JS 开发体验的团队。

3. 设计系统集成

需要集中管理设计令牌(design tokens),并在组件库中直接以 JS 变量形式引用颜色、间距、字体等:

import { tokens } from '@my-org/design-tokens';
 
const Card = styled.div`
  background: ${tokens.colors.surface};
  border-radius: ${tokens.radii.md};
  padding: ${tokens.spacing.lg};
`;

4. SSR / SSG 应用

零运行时方案天然支持 SSR/SSG,无需额外的样式注入配置。构建时提取的 CSS 文件可直接用于服务端渲染,配合 Critical CSS 提取实现首屏优化。

5. 大型 Monorepo 项目

需要跨多个应用/包共享样式逻辑,Linaria 的提取机制避免了运行时依赖的版本冲突和重复打包问题。

6. 需要 Stylelint 的团队

已有 CSS 规范(Stylelint 配置)的团队,Linaria 允许在不放弃 JS 样式的前提下继续使用现有 Lint 规则。

7. 不支持 JS 的环境降级

在某些 JS 受限场景(如邮件模板、嵌入式 WebView),Linaria 提取出的纯 CSS 文件可以直接使用,而传统 CSS-in-JS 方案完全无法工作。


开发与工程化

TypeScript 支持

Linaria v8 使用 TypeScript 编写,提供完整的类型定义。styled 组件支持泛型传入 props 类型:

interface ButtonProps {
  variant: 'primary' | 'secondary';
  size: 'sm' | 'md' | 'lg';
}
 
const Button = styled.button<ButtonProps>`
  padding: ${props => props.size === 'sm' ? '4px 8px' : '8px 16px'};
  background: ${props => props.variant === 'primary' ? '#007bff' : '#6c757d'};
  color: white;
  border: none;
  border-radius: 4px;
`;

主题方案

Linaria 支持多种主题实现方式:

// 方式一:CSS 变量主题(推荐)
const theme = {
  primary: 'var(--color-primary)',
  secondary: 'var(--color-secondary)',
};
 
// 方式二:动态 props 主题
const ThemedButton = styled.button<{ theme: Theme }>`
  background-color: ${props => props.theme.primary};
  color: ${props => props.theme.text};
`;

测试

Linaria 组件可以在 Jest / Vitest 中正常测试,需要配置相应的 transform:

// jest.config.ts
export default {
  transform: {
    '^.+\\.[jt]sx?$': [
      'babel-jest',
      {
        presets: [
          '@babel/preset-env',
          '@babel/preset-react',
          '@wyw-in-js/babel-preset',
        ],
      },
    ],
  },
};

ESLint 集成

Linaria 样式定义遵循标准 JS 语法,可配合 ESLint 的模板字符串规则使用。Stylelint 负责 CSS 部分的规范检查。

Monorepo 工作流

Linaria 支持在 pnpm workspace / Turborepo 等 Monorepo 工具中使用,各包的样式会被独立提取并合并。


性能与安全

性能优势

指标Linariastyled-components (运行时)
运行时 JS 体积0 KB(CSS 已提取)~12 KB(运行时逻辑)
CSS 缓存浏览器原生缓存 .css 文件不可缓存(运行时生成)
首屏渲染CSS 文件 &lt;link&gt; 预加载等待 JS 执行后注入 &lt;style&gt;
样式计算零运行时计算每次渲染执行模板求值
内存占用无额外内存开销运行时维护样式缓存
FID / INP 影响无影响有微小影响

构建性能

  • wyw-in-js v2 的混合求值策略减少了不必要的求值回退,提升了构建速度。
  • 支持增量编译和缓存(依赖 bundler 实现)。
  • CSS 提取过程可并行化,不会成为构建瓶颈。

安全考量

  • XSS 防护:Linaria 的模板字符串编译阶段会对动态值进行转义,但应避免直接插入用户输入的原始 CSS 字符串。
  • CSS 注入:由于样式在构建时提取,运行时不存在 CSS 注入攻击面。动态样式通过 CSS 变量设置,受限于 style 属性的安全边界。
  • 依赖安全:样式中引用的模块在构建时求值,不会引入运行时外部依赖。

技术对比

零运行时 CSS-in-JS 方案对比

特性Linariavanilla-extractPanda CSSCSS Modules
零运行时
API 风格tagged template literal.css.ts 文件 + style()tagged template + recipes.module.css 文件
语法熟悉度⭐⭐⭐⭐⭐(与 SC/Emotion 一致)⭐⭐⭐(需学习新 API)⭐⭐⭐⭐⭐⭐⭐⭐⭐
TypeScript 类型安全⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
动态样式✅ CSS 变量✅ CSS 变量 + 条件类✅ 条件样式❌ 需手动管理
Sass-like 嵌套✅ 原生支持✅ 通过 style 对象
原子化 CSS@linaria/atomic❌(需手动)✅ 默认原子化
框架绑定React(可扩展)框架无关React / Vue / Svelte框架无关
Stylelint 支持✅ 原生
迁移成本(从 SC)⭐⭐⭐⭐⭐ 极低⭐⭐⭐ 中等⭐⭐⭐ 中等⭐⭐ 较高
GitHub Stars~12.3k~9.5k~5.5kN/A(Next.js 内置)

Linaria vs styled-components

维度Linariastyled-components
运行时零运行时~12 KB 运行时
CSS 输出独立 .css 文件运行时 &lt;style&gt; 标签
APIcss / styled 标签styled 标签
动态样式CSS 变量(编译时生成规则)JS 运行时求值
SSR天然支持(CSS 文件)ServerStyleSheet
CSS Source Map✅ 完整✅ 开发环境
Stylelint✅ 原生支持❌ 需额外插件
生态成熟度⭐⭐⭐⭐⭐⭐⭐⭐⭐(但已停止维护)
迁移难度低(API 相似)N/A

注意:styled-components 已于 2025 年宣布不再积极维护,Linaria 是其推荐迁移方案之一。


最佳实践

1. 样式与逻辑分离

将复杂样式逻辑封装为独立的 styled 组件,避免在业务组件中混合过多样式定义:

// ✅ 推荐:独立封装
const Card = styled.div`...`;
const CardTitle = styled.h2`...`;
const CardBody = styled.div`...`;
 
// ❌ 不推荐:所有样式内联在一个组件
const Page = () => (
  <div css={`...`}>
    <h2 css={`...`}>...</h2>
  </div>
);

2. 设计令牌集中管理

将颜色、间距、字体等设计令牌提取到独立的 JS 模块中,供 Linaria 样式直接引用:

// tokens.ts
export const tokens = {
  colors: { primary: '#007bff', text: '#333' },
  spacing: { sm: 8, md: 16, lg: 24 },
  radii: { sm: 4, md: 8 },
} as const;

3. 避免构建时无法求值的表达式

确保样式中引用的模块没有副作用(side-effects),且表达式可被静态分析:

// ✅ 推荐:纯函数、常量
import { rem } from 'polished';
const padding = rem(16); // 构建时可求值
 
// ❌ 避免:依赖运行时状态
const color = getComputedThemeColor(); // 构建时无法求值

4. 配合 CSS 变量实现主题切换

// 在根元素设置 CSS 变量
document.documentElement.style.setProperty('--color-primary', '#007bff');
 
// Linaria 样式中引用
const Button = styled.button`
  background: var(--color-primary);
`;

5. 使用 @linaria/interop 处理迁移过渡

从 styled-components 迁移时,使用 @linaria/interop 确保选择器互操作:

import { styled as linariaStyled } from '@linaria/react';
import { styled as scStyled } from 'styled-components';
import { interop } from '@linaria/interop';
 
// 确保 Linaria 组件能正确引用 styled-components 组件的选择器

6. 启用 Stylelint 检查

在团队项目中统一 CSS 规范,利用 Stylelint 在 CI 阶段检查样式质量。

7. 利用原子化样式减小产物体积

对高频使用的原子样式(如 margin、padding、color),使用 @linaria/atomic 生成原子类以最大化复用。


技术局限与边界

1. 动态样式限制

css 标签 不支持动态样式——只有 styled 组件可以使用 props 动态表达式。若需要在 css 中使用动态值,必须改用 styled 或手动管理 CSS 变量。

// ❌ 不支持
const myClass = css`
  color: ${props => props.color}; // css 标签不支持 props
`;
 
// ✅ 正确做法
const MyComponent = styled.div`
  color: ${props => props.color}; // styled 标签支持
`;

2. 模块求值限制

样式中引用的模块 必须可被静态求值。如果模块有副作用、依赖浏览器 API(如 windowdocument),或需要运行时环境才能正常工作,则无法在构建时被正确求值。

// ❌ 会失败:依赖浏览器 API
const color = window.localStorage.getItem('theme-color');
const Box = styled.div`
  background: ${color};
`;

3. Node.js 版本要求

Linaria v8 要求 Node.js >= 22.12.0,部分老旧 CI 环境或 LTS 版本可能需要升级。

4. IE11 不支持

动态样式基于 CSS 自定义属性(CSS Variables)实现,IE11 不支持此特性。若需兼容 IE11,需使用 polyfill 或避免使用动态样式。

5. Babel 配置依赖

Linaria 深度依赖 Babel 编译管线,若项目使用非 Babel 工具链(如纯 SWC、esbuild 原生模式),需要额外的适配器或降级方案。

6. 构建复杂度

引入 Linaria 意味着增加了一层编译提取步骤,可能增加构建配置的复杂度,特别是在 monorepo、多框架混用的场景下。

7. 非 CSS 产物无法提取

Linaria 只能提取 CSS,如果需要在 JS 中获取编译后的类名用于非 DOM 场景(如 Canvas 绘制),需要额外处理。

8. 第三方组件样式覆盖

覆盖第三方库样式时,由于 Linaria 提取的类名是哈希化的,需要使用 :global() 选择器或 @linaria/interop 来处理跨库选择器引用。


学习资源

官方文档

技术博客

社区资源

视频与演讲

  • Callstack 团队在 React Native EU、React Conf 等会议上的 CSS-in-JS 相关演讲
  • YouTube 搜索 "Linaria CSS-in-JS" 可查看社区教程与实战演示

2026 年现状

版本演进

Linaria 于 2026 年 6 月发布了 v8.0.0,主要变更包括:

  • Node.js >= 22.12.0:要求更高版本的 Node.js 运行时。
  • wyw-in-js v2:底层编译引擎升级,默认采用混合求值策略(eval.strategy: 'hybrid'),提升构建稳定性。
  • 迁移支持:提供完整的 迁移指南稳定性说明

行业趋势

2025—2026 年,CSS-in-JS 领域发生了显著变化:

  1. styled-components 停止维护:2025 年,styled-components 被宣布不再积极维护,大量团队开始迁移。Linaria 因 API 相似性成为首选迁移目标。
  2. 零运行时方案崛起:Linaria、vanilla-extract、Panda CSS 等零运行时方案获得越来越多关注,性能与缓存优势在大型应用中日益凸显。
  3. CSS-in-JS 反思:社区对"CSS-in-JS 是否已死"的讨论趋于理性,结论是:运行时 CSS-in-JS 的适用场景在收窄,但零运行时方案正在成为新标准。
  4. Atomic CSS 融合:Linaria 通过 @linaria/atomic 拥抱原子化 CSS 趋势,与 Tailwind CSS 的理念形成互补。

维护状态

Linaria 由 Callstack 团队持续维护,GitHub 仓库活跃(12.3k stars,589 次发布),核心贡献者稳定。作为零运行时 CSS-in-JS 领域最成熟的方案之一,Linaria 在 2026 年仍然是高性能 Web 应用的重要样式选择。


总结:Linaria 是零运行时 CSS-in-JS 领域的标杆项目。它在保留 CSS-in-JS 开发体验的同时,通过构建时提取消除了运行时开销,是 styled-components 迁移和性能敏感场景的理想选择。随着 2025—2026 年行业向零运行时方案倾斜,Linaria 的战略地位进一步提升。