styled-components vs emotion vs sass vs styled-jsx
React エコシステムにおける CSS 設計戦略
styled-componentsemotionsassstyled-jsx類似パッケージ:

React エコシステムにおける CSS 設計戦略

emotionsassstyled-componentsstyled-jsx は、ウェブアプリケーションのスタイリングを異なるアプローチで解決する主要なライブラリです。sass は CSS に変数やネスト機能を追加する従来のプリアプロセッサであり、ビルド時に CSS を生成します。一方、emotionstyled-components は CSS-in-JS ライブラリで、JavaScript 内でスタイルを定義し、ランタイムまたはビルド時に CSS を注入します。styled-jsx は React コンポーネント内に CSS を直接記述できるスコープ付き CSS ソリューションで、特に Next.js との相性が抜群です。これらはそれぞれ、開発者の好み、プロジェクトの規模、パフォーマンス要件に応じて選定されます。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
styled-components10,781,02541,0242.02 MB102日前MIT
emotion0---6年前MIT
sass04,1925.93 MB692ヶ月前MIT
styled-jsx07,7901.03 MB841年前MIT

React エコシステムにおける CSS 設計戦略:emotion vs sass vs styled-components vs styled-jsx

ウェブ開発において、スタイルの管理方法はアプリケーションの保守性やパフォーマンスに直結します。emotionsassstyled-componentsstyled-jsx はそれぞれ異なる哲学を持っており、どれを選ぶべきかはプロジェクトの要件によります。ここでは、実開発で直面する具体的なシナリオに基づき、これら 4 つのライブラリを技術的に比較します。

🎨 基本的な構文:スタイルの定義方法

スタイルをどのように記述するかは、開発体験(DX)に最も影響します。

sass は独立した .scss ファイルでスタイルを定義します。

  • CSS と同じ構文に、変数やネスト機能を追加できます。
  • ファイル名でコンポーネントを紐付けるのが一般的です。
/* button.scss */
$primary-color: #0070f3;

.button {
  background: $primary-color;
  padding: 10px 20px;
  
  &:hover {
    opacity: 0.9;
  }
}

emotion は JavaScript 内で css 関数または styled を使います。

  • 動的なプロパティに基づいてスタイルを生成できます。
  • css プロパティを使ってコンポーネントに適用します。
// Emotion
import { css } from '@emotion/react';

const style = css({
  backgroundColor: '#0070f3',
  padding: '10px 20px',
  '&:hover': { opacity: 0.9 }
});

function Button() {
  return <button css={style}>Click</button>;
}

styled-components はタグ付きテンプレートリテラルを使用します。

  • 実際の CSS 構文を JavaScript 内に書けます。
  • スタイル付きコンポーネントを新規作成します。
// styled-components
import styled from 'styled-components';

const Button = styled.button`
  background: #0070f3;
  padding: 10px 20px;
  &:hover { opacity: 0.9; }
`;

function App() {
  return <Button>Click</Button>;
}

styled-jsx はコンポーネント内に <style jsx> タグを埋め込みます。

  • 標準の CSS 構文が使えます。
  • コンポーネントのローカルスコープに自動で制限されます。
// styled-jsx
function Button() {
  return (
    <>
      <button>Click</button>
      <style jsx>{`
        button {
          background: #0070f3;
          padding: 10px 20px;
        }
        button:hover {
          opacity: 0.9;
        }
      `}</style>
    </>
  );
}

🧵 テーマと変数:デザインの統一管理

大規模プロジェクトでは、色やフォントを一元管理する必要があります。

sass はグローバル変数ファイルを読み込みます。

  • ビルド時に値が埋め込まれるため、ランタイムコストはありません。
  • 変更を反映するには再ビルドが必要です。
/* _variables.scss */
$brand-color: #0070f3;

/* main.scss */
@import 'variables';

.box { color: $brand-color; }

emotionThemeProvider を使って動的なテーマを提供します。

  • ランタイムでテーマを変更可能です(ダークモードなど)。
  • theme オブジェクトをスタイル関数から参照します。
// Emotion
import { ThemeProvider, css } from '@emotion/react';

const theme = { colors: { primary: '#0070f3' } };

function ThemedBox() {
  return (
    <ThemeProvider theme={theme}>
      <div css={t => css({ color: t.colors.primary })}>Text</div>
    </ThemeProvider>
  );
}

styled-componentsThemeProvider を採用しています。

  • 構文はテンプレートリテラル内で props.theme を使う形です。
  • 型定義と組み合わせることで、安全にテーマプロパティを参照できます。
// styled-components
import { ThemeProvider } from 'styled-components';

const theme = { colors: { primary: '#0070f3' } };

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

function App() {
  return <ThemeProvider theme={theme}><Box>Text</Box></ThemeProvider>;
}

styled-jsx はテーマ機能を提供しません。

  • CSS 変数(Custom Properties)を使って疑似的なテーマを実現します。
  • グローバルスコープの変数をコンポーネント内で参照する形になります。
// styled-jsx
function ThemedBox() {
  return (
    <>
      <div className="box">Text</div>
      <style jsx>{`
        .box {
          color: var(--brand-color);
        }
      `}</style>
      <style global>{`
        :root { --brand-color: #0070f3; }
      `}</style>
    </>
  );
}

🛡️ スコープと隔離:スタイルの衝突防止

スタイルが意図せず他の要素に影響を与えないようにすることは重要です。

sass はデフォルトでグローバルスコープです。

  • クラス名の衝突を防ぐには、BEM 命名規則や CSS Modules との併用が必要です。
  • 設定なしでは、定義したスタイルが全体に適用されます。
/* sass (Global by default) */
.button { /* Affects all .button elements */ }

emotion は自動でユニークなクラス名を生成します。

  • 定義したスタイルはそのコンポーネントにのみ適用されます。
  • 明示的にグローバルスタイルを指定しない限り、漏れ出しません。
// Emotion (Scoped)
const className = css({ color: 'red' });
// Renders class like "css-12345", scoped to this usage

styled-components も同様に自動スコーピングを行います。

  • 生成されるクラス名はハッシュ化されており、衝突の心配がありません。
  • コンポーネントの削除とともにスタイルも消えるため、整理しやすいです。
// styled-components (Scoped)
const StyledButton = styled.button` color: red; `;
// Renders class like "sc-bdVaJa", scoped to component

styled-jsx はコンポーネントローカルなスコープを強制します。

  • <style jsx> 内のスタイルは、そのコンポーネントの JSX にのみ適用されます。
  • 子コンポーネントにスタイルを浸透させるには :global() が必要です。
// styled-jsx (Scoped)
function Component() {
  return (
    <div>
      <style jsx>{`div { color: red; }`}</style>
    </div>
  );
}

⚡ パフォーマンスと SSR:表示速度と最適化

サーバーサイドレンダリング(SSR)環境での挙動は、SEO や初期表示速度に影響します。

sass はビルド時に静的な CSS ファイルを出力します。

  • ブラウザは通常の CSS ファイルとして読み込むため、処理コストが最小です。
  • SSR 環境でも、ビルド済みの CSS をリンクするだけで済みます。
<!-- sass output -->
<link rel="stylesheet" href="/static/styles.css" />

emotion は SSR 時にスタイルを抽出して HTML に埋め込むことができます。

  • @emotion/server を使うことで、FOUC(スタイル未適用での点滅)を防げます。
  • ランタイムでスタイルを注入する処理がわずかに発生します。
// Emotion SSR
import { extractCritical } from '@emotion/server';
// Extract styles during render and inject into <head>

styled-components も SSR サポートが充実しています。

  • StyleSheetManager や SSR 用プラグインを使って、スタイルを事前に収集します。
  • 初期ロード時にスタイルタグを HTML に含めることで、表示崩れを防ぎます。
// styled-components SSR
import { ServerStyleSheet } from 'styled-components';
// Collect styles during render and inject style tags

styled-jsx はビルド時に CSS を解決するハイブリッドなアプローチです。

  • 可能な限り静的な CSS として出力し、ランタイム処理を減らします。
  • Next.js との統合により、設定なしで最適な SSR 設定が適用されます。
// styled-jsx (Next.js)
// Styles are extracted to separate CSS files during build automatically
// No extra runtime configuration needed for SSR

📊 总结:主要な違い

特徴sassemotionstyled-componentsstyled-jsx
構文SCSS ファイルJS オブジェクト/テンプレートテンプレートリテラルJSX 内 <style> タグ
スコープグローバル (要設定)自動スコープ自動スコープコンポーネントローカル
テーマ変数 (ビルド時)ThemeProvider (ランタイム)ThemeProvider (ランタイム)CSS 変数 (手動)
SSR静的 CSS抽出可能抽出可能自動最適化 (Next.js)
依存なし (CSS 出力)ランタイムありランタイムありビルド時解決

💡 結論:プロジェクトに合わせた選択

sass は、伝統的な CSS 開発フローを維持したい場合や、ランタイムオーバーヘッドを完全に排除したい場合に最適です。既存のデザインシステムや、CSS 専門のチームがいる環境で力を発揮します。

emotion は、柔軟性と機能性のバランスを求める場合に適しています。React 以外のフレームワークとの共存や、高度な動的スタイリングが必要な複雑なアプリケーションで選ばれます。

styled-components は、React 生態系に深く統合された開発体験を提供します。コンポーネントとスタイルを一体化させ、チーム内で一貫したコーディングスタイルを強制したい場合に有効です。

styled-jsx は、Next.js を使用するプロジェクトにおけるデファクトスタンダードです。設定の手間をかけずに、パフォーマンスと開発効率の両方を得たい場合に最も合理的な選択となります。

最終的には、チームの習熟度と、アプリケーションが求めるパフォーマンス要件のバランスを見て決定することが重要です。

選び方: styled-components vs emotion vs sass vs styled-jsx

  • styled-components:

    styled-components を選ぶべきなのは、React 中心の開発を行っており、コンポーネントとスタイルを完全に一体化させたい場合です。タグ付きテンプレートリテラルによる直感的な構文と、強力な TypeScript サポートが特徴です。スタイルがコンポーネントに密結合するため、コンポーネントの移植性が高まり、大規模な React アプリケーションで維持管理しやすい構造を作れます。

  • emotion:

    emotion を選ぶべきなのは、React 以外のフレームワークでもスタイルを共有したい場合や、css プロパティによる柔軟なスタイリングが必要な場合です。ランタイムのパフォーマンスと機能性のバランスが良く、サーバーサイドレンダリング(SSR)のサポートも充実しています。大規模なシステムで、コンポーネントのスタイルを動的に制御する必要があるプロジェクトに適しています。

  • sass:

    sass を選ぶべきなのは、既存の CSS 資産を継承する場合や、チームが CSS 分離の原則を好む場合です。ビルド時に CSS が生成されるため、ランタイムでの JavaScript 処理が不要で、パフォーマンス面で有利です。CSS 変数やミックスインなど、成熟した機能セットを必要とする伝統的な開発スタイルに最適です。

  • styled-jsx:

    styled-jsx を選ぶべきなのは、Next.js を使用しており、ビルド時に CSS を解決してランタイムオーバーヘッドを減らしたい場合です。コンポーネントローカルなスコープ付き CSS を標準の <style> タグで記述できるため、学習コストが低く、ブラウザのネイティブ機能に近い挙動をします。Vercel 環境や Next.js プロジェクトでは最も統合しやすい選択肢です。

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.