T-CREATOR

Emotion とは?次世代 CSS-in-JS の魅力を徹底解説

Emotion とは?次世代 CSS-in-JS の魅力を徹底解説

モダンフロントエンド開発において、スタイリングの手法は大きく進化しています。従来の CSS ファイルによるスタイリングから、CSS-in-JS という新しいアプローチが注目を集めており、その中でも Emotion は特に多くの開発者に愛用されています。

本記事では、Emotion の基本概念から実際の使い方、他のライブラリとの比較まで、初心者の方でも理解しやすいように丁寧に解説いたします。実際にコードを書きながら、Emotion の魅力を体感していただけるでしょう。

CSS-in-JS とは何か?従来の CSS との違い

CSS-in-JS とは、JavaScript の中に CSS を記述する技術のことです。従来の CSS ファイルとは異なり、コンポーネントと密接に結合したスタイリングが可能になります。

従来の CSS の課題

従来の CSS には以下のような課題がありました:

項目従来の CSSCSS-in-JS
スコープグローバルコンポーネント単位
動的スタイリング困難簡単
メンテナンス性低い高い
TypeScript 対応限定的完全対応

従来の CSS では、以下のような問題が発生しやすかったのです:

css/* グローバルスタイルで意図しない影響が発生 */
.button {
  background-color: blue;
  color: white;
}

/* 別のコンポーネントでも同じクラス名を使用してしまう */
.button {
  background-color: red; /* 意図しない上書き */
}

上記のコードでは、クラス名の衝突により意図しないスタイルの上書きが発生してしまいます。

CSS-in-JS の利点

CSS-in-JS では、これらの問題を解決できます:

  • コンポーネントスコープ: スタイルが特定のコンポーネントに限定される
  • 動的スタイリング: JavaScript の変数や関数を使用できる
  • 型安全性: TypeScript との連携が容易
  • デッドコード排除: 使用されていないスタイルが自動的に削除される

Emotion の基本概念と特徴

Emotion は、パフォーマンスと開発体験を重視した CSS-in-JS ライブラリです。React はもちろん、Vue.js や Angular でも使用できる柔軟性が特徴です。

Emotion の主な特徴

Emotion には以下の特徴があります:

  1. 高いパフォーマンス: 最適化されたランタイム処理
  2. 開発体験の向上: 優れたエラーメッセージとデバッグ支援
  3. 柔軟な書き方: 複数の API 提供
  4. TypeScript 完全対応: 型安全なスタイリング

Emotion の 2 つのアプローチ

Emotion では、主に 2 つのアプローチでスタイリングを行えます:

1. CSS Prop アプローチ JSX のcssプロパティを使用してスタイルを定義します:

jsx/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const buttonStyle = css`
  background-color: #007bff;
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
`;

function Button() {
  return <button css={buttonStyle}>クリック</button>;
}

2. Styled Components アプローチ styledを使用してスタイル付きコンポーネントを作成します:

jsximport styled from '@emotion/styled';

const StyledButton = styled.button`
  background-color: #007bff;
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
`;

function Button() {
  return <StyledButton>クリック</StyledButton>;
}

なぜ Emotion が注目されているのか?

Emotion が多くの開発者に注目される理由は、以下の点にあります。

1. パフォーマンスの優秀さ

Emotion は実行時の最適化が優れており、不要なスタイルの再計算を避けます。実際のベンチマークでは、他の CSS-in-JS ライブラリと比較して高いパフォーマンスを示しています。

2. 開発者体験の向上

エラーメッセージが分かりやすく、デバッグが容易です。例えば、よくある間違いとして以下のようなエラーが発生します:

vbnetError: You have tried to stringify object returned from `css` function.
It isn't supposed to be used directly (e.g. as value of the `className` prop),
but rather handed to emotion so it can handle it (e.g. as value of `css` prop).

このエラーは、css関数の戻り値を直接classNameに使用しようとした際に発生します。正しい使い方は以下の通りです:

jsx// ❌ 間違い
const style = css`
  color: red;
`;
<div className={style}>テキスト</div>;

// ✅ 正しい
const style = css`
  color: red;
`;
<div css={style}>テキスト</div>;

3. 豊富なエコシステム

Emotion には豊富なプラグインやユーティリティが提供されており、開発効率が向上します。

Emotion の基本的な使い方

実際に Emotion を使い始めるための手順を見ていきましょう。

インストール

まず、必要なパッケージをインストールします:

bash# React用の基本パッケージ
yarn add @emotion/react @emotion/styled

# TypeScript使用時は型定義も追加
yarn add -D @types/react

基本的なセットアップ

1. Babel 設定 .babelrcまたはbabel.config.jsに以下を追加:

json{
  "presets": [
    [
      "@babel/preset-react",
      {
        "runtime": "automatic",
        "importSource": "@emotion/react"
      }
    ]
  ]
}

2. TypeScript 設定 tsconfig.jsonに以下を追加:

json{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@emotion/react"
  }
}

最初のコンポーネント

簡単なボタンコンポーネントを作成してみましょう:

jsx/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const buttonStyle = css`
  background-color: #007bff;
  color: white;
  padding: 12px 24px;
  border: none;
  border-radius: 6px;
  font-size: 16px;
  cursor: pointer;
  transition: background-color 0.2s ease;

  &:hover {
    background-color: #0056b3;
  }
`;

このコードでは、基本的なボタンスタイルを定義しています。&:hoverセレクタを使用してホバー時の効果も追加しています。

実際に Emotion を使ってみよう

実際にハンズオンで Emotion を体験してみましょう。簡単なカードコンポーネントを作成します。

プロジェクトの作成

新しい React プロジェクトを作成します:

bashyarn create react-app emotion-sample --template typescript
cd emotion-sample
yarn add @emotion/react @emotion/styled

カードコンポーネントの作成

美しいカードコンポーネントを作成してみましょう:

jsx/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import styled from '@emotion/styled';

// CSS Propアプローチでカードスタイルを定義
const cardStyle = css`
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  padding: 24px;
  margin: 16px 0;
  max-width: 400px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;

  &:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
  }
`;

// Styled Componentsアプローチでタイトルを定義
const CardTitle = styled.h3`
  color: #2c3e50;
  margin: 0 0 16px 0;
  font-size: 1.25rem;
  font-weight: 600;
`;

const CardContent = styled.p`
  color: #7f8c8d;
  line-height: 1.6;
  margin: 0;
`;

コンポーネントの使用

作成したスタイルを使用してカードコンポーネントを完成させます:

jsxinterface CardProps {
  title: string;
  content: string;
}

function Card({ title, content }: CardProps) {
  return (
    <div css={cardStyle}>
      <CardTitle>{title}</CardTitle>
      <CardContent>{content}</CardContent>
    </div>
  );
}

function App() {
  return (
    <div>
      <Card
        title='Emotionの魅力'
        content='Emotionを使用することで、コンポーネントベースのスタイリングが可能になります。'
      />
      <Card
        title='高いパフォーマンス'
        content='最適化されたランタイム処理により、高速な描画が実現されます。'
      />
    </div>
  );
}

よくあるエラーとその解決方法

実際の開発でよく遭遇するエラーと解決方法をご紹介します:

1. JSXImportSource エラー

javascript'React' refers to a UMD global, but the current file is a module.
Consider adding an import instead.

解決方法:ファイルの先頭に以下を追加

jsx/** @jsxImportSource @emotion/react */

2. TypeScript エラー

bashProperty 'css' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'

解決方法:型定義を追加

tsx/// <reference types="@emotion/react/types/css-prop" />

スタイリングの基本パターン

Emotion でよく使用されるスタイリングパターンをご紹介します。

1. 動的スタイリング

Props に基づいてスタイルを動的に変更できます:

jsximport styled from '@emotion/styled';

interface ButtonProps {
  variant: 'primary' | 'secondary' | 'danger';
  size: 'small' | 'medium' | 'large';
}

const Button =
  styled.button <
  ButtonProps >
  `
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: 500;
  transition: all 0.2s ease;
  
  /* サイズの設定 */
  ${({ size }) => {
    switch (size) {
      case 'small':
        return css`
          padding: 6px 12px;
          font-size: 14px;
        `;
      case 'large':
        return css`
          padding: 12px 24px;
          font-size: 18px;
        `;
      default:
        return css`
          padding: 8px 16px;
          font-size: 16px;
        `;
    }
  }}
  
  /* バリアントの設定 */
  ${({ variant }) => {
    switch (variant) {
      case 'primary':
        return css`
          background-color: #007bff;
          color: white;
          &:hover {
            background-color: #0056b3;
          }
        `;
      case 'danger':
        return css`
          background-color: #dc3545;
          color: white;
          &:hover {
            background-color: #c82333;
          }
        `;
      default:
        return css`
          background-color: #6c757d;
          color: white;
          &:hover {
            background-color: #5a6268;
          }
        `;
    }
  }}
`;

2. テーマの活用

テーマを使用して一貫したデザインシステムを構築できます:

jsximport { ThemeProvider } from '@emotion/react';

const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    danger: '#dc3545',
    success: '#28a745',
  },
  spacing: {
    xs: '4px',
    sm: '8px',
    md: '16px',
    lg: '24px',
    xl: '32px',
  },
  breakpoints: {
    mobile: '768px',
    tablet: '1024px',
    desktop: '1200px',
  },
};

function App() {
  return (
    <ThemeProvider theme={theme}>
      <div>{/* アプリケーションのコンテンツ */}</div>
    </ThemeProvider>
  );
}

テーマを使用したコンポーネントの例:

jsxconst ThemedButton = styled.button`
  background-color: ${({ theme }) => theme.colors.primary};
  padding: ${({ theme }) => theme.spacing.md};
  color: white;
  border: none;
  border-radius: 4px;

  @media (max-width: ${({ theme }) =>
      theme.breakpoints.mobile}) {
    padding: ${({ theme }) => theme.spacing.sm};
    font-size: 14px;
  }
`;

3. メディアクエリとレスポンシブデザイン

レスポンシブデザインも簡単に実装できます:

jsxconst ResponsiveGrid = styled.div`
  display: grid;
  gap: 16px;
  padding: 16px;

  /* モバイル: 1列 */
  grid-template-columns: 1fr;

  /* タブレット: 2列 */
  @media (min-width: 768px) {
    grid-template-columns: repeat(2, 1fr);
  }

  /* デスクトップ: 3列 */
  @media (min-width: 1024px) {
    grid-template-columns: repeat(3, 1fr);
  }
`;

他の CSS-in-JS ライブラリとの比較

Emotion と StyledComponents の比較を詳しく見てみましょう。

パフォーマンス比較

項目EmotionStyledComponents
バンドルサイズ小さい大きい
実行速度高速普通
メモリ使用量少ない多い

機能比較

Emotion の特徴:

  • CSS Prop アプローチが可能
  • 優れた TypeScript 対応
  • 豊富なプラグインエコシステム
  • 軽量で高速

StyledComponents の特徴:

  • 豊富なコミュニティ
  • 成熟したエコシステム
  • 詳細なドキュメント

移行のしやすさ

StyledComponents から Emotion への移行は比較的簡単です:

jsx// StyledComponents
import styled from 'styled-components';

const Button = styled.button`
  background-color: blue;
  color: white;
`;

// Emotion (ほぼ同じ書き方)
import styled from '@emotion/styled';

const Button = styled.button`
  background-color: blue;
  color: white;
`;

エラーハンドリングの違い

Emotion は、より詳細なエラーメッセージを提供します:

typescript// Emotionのエラーメッセージ例
Error: The `css` function must be called with a template literal or an object.
Received: undefined

You might have forgotten to import the `css` function:
import { css } from '@emotion/react'

このような親切なエラーメッセージにより、開発時の問題解決が迅速になります。

まとめ

Emotion は、現代の React 開発において非常に有用な CSS-in-JS ライブラリです。本記事では、以下の内容について詳しく解説いたしました:

学習したポイント

  1. CSS-in-JS の基本概念: 従来の CSS との違いと利点
  2. Emotion の特徴: 高パフォーマンス、優れた開発体験
  3. 基本的な使い方: セットアップから実際のコンポーネント作成まで
  4. スタイリングパターン: 動的スタイリング、テーマ活用、レスポンシブデザイン
  5. 他ライブラリとの比較: StyledComponents との違い

次のステップ

Emotion を習得したら、以下のような発展的な内容にも挑戦してみてください:

  • SSR 対応: Next.js でのサーバーサイドレンダリング
  • パフォーマンス最適化: バンドルサイズの最適化
  • デザインシステム構築: 大規模アプリケーションでの活用

Emotion を使用することで、より保守性が高く、パフォーマンスに優れた React アプリケーションを開発できるでしょう。ぜひ実際のプロジェクトで活用してみてください。

関連リンク