T-CREATOR

Emotion 入門:3 分でわかるセットアップと最初の一歩

Emotion 入門:3 分でわかるセットアップと最初の一歩

現代の React 開発において、スタイリング手法の選択は開発体験を大きく左右します。CSS Modules、styled-components、そして今回ご紹介する Emotion。これらの中でも、Emotion は特に注目を集めている CSS-in-JS ライブラリです。

「3 分で Emotion を始められるの?」と疑問に思われるかもしれませんが、実際にその通りなのです。Emotion は学習コストが低く、既存のプロジェクトにも段階的に導入できる優れた特徴を持っています。

本記事では、Emotion の基本的な概念から実際のセットアップ、そして最初のコンポーネント作成まで、わずか 3 分で体験できるように構成いたしました。初心者の方でも安心して取り組めるよう、よくあるエラーの対処法も含めて詳しく解説していきます。

背景

CSS-in-JS ライブラリの必要性

現代の Web 開発では、コンポーネントベースの開発が主流となっています。React、Vue、Angular など、多くのフレームワークがこの手法を採用していますね。

しかし、従来の CSS は「グローバルスコープ」という特徴があり、コンポーネントベース開発との相性に課題がありました。

項目従来の CSSCSS-in-JS
スコープグローバルコンポーネント単位
動的スタイル困難簡単
TypeScript 連携限定的完全対応
バンドル最適化手動自動

従来の CSS の課題と現代的な解決策

従来の CSS 管理で直面する主な課題は以下の通りです:

グローバル名前空間の競合 多くの開発者が経験する問題として、CSS クラス名の競合があります。特に大規模なプロジェクトでは、意図しないスタイルの上書きが発生することがあります。

コンポーネントとスタイルの分離 JSX ファイルと CSS ファイルが別々に管理されることで、コンポーネントの移動や削除時にスタイルが残ってしまう問題も頻繁に発生します。

動的スタイリングの複雑さ props や state に基づいてスタイルを変更する際、従来の CSS では複雑な条件分岐が必要になることがあります。

CSS-in-JS ライブラリは、これらの課題を根本的に解決する現代的なアプローチを提供しています。

課題

スタイリング管理の複雑さ

実際の開発現場では、以下のような問題に直面することがあります:

CSS Modules の場合

typescript// Button.module.css
.button {
  background: blue;
  color: white;
}

.button--primary {
  background: red;
}

// Button.tsx
import styles from './Button.module.css';

const Button = ({ primary, children }) => {
  return (
    <button
      className={`${styles.button} ${primary ? styles['button--primary'] : ''}`}
    >
      {children}
    </button>
  );
};

このアプローチでは、クラス名の管理が煩雑になり、動的なスタイリングが困難になります。

コンポーネントベース開発での CSS 管理の問題

React 開発でよく発生する問題の一つが、コンポーネントの再利用性とスタイルの管理です。

よくある問題例

typescript// 問題のあるコード例
const Card = ({ isActive, variant }) => {
  // 複雑な条件分岐
  const getClassName = () => {
    let className = 'card';
    if (isActive) className += ' card--active';
    if (variant === 'primary')
      className += ' card--primary';
    if (variant === 'secondary')
      className += ' card--secondary';
    return className;
  };

  return <div className={getClassName()}>...</div>;
};

このような実装では、スタイルの管理が複雑になり、バグの原因となることがあります。

解決策

Emotion が提供する機能とメリット

Emotion は、上記の課題を解決するための強力な機能を提供します:

機能説明メリット
CSS PropJSX 内で直接スタイルを記述直感的な書き方
Styled Componentsスタイル付きコンポーネントの作成再利用性の向上
Theme Providerテーマ機能一貫したデザインシステム
TypeScript 対応型安全なスタイリング開発時のエラー防止

主要なメリット

  1. 自動的なスコープ管理 クラス名の競合を自動的に防ぎ、コンポーネント単位でのスタイル管理が可能です。

  2. 動的スタイリングの簡素化 props や state に基づいたスタイルの変更が直感的に行えます。

  3. 優れた開発体験 TypeScript との連携により、スタイルプロパティの補完やエラーチェックが利用できます。

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

現在、主要な CSS-in-JS ライブラリとして以下があります:

ライブラリバンドルサイズ学習コストTypeScript 対応特徴
Emotion小さい低い優秀柔軟性が高い
styled-components中程度低い良好豊富な機能
JSS大きい高い良好設定が複雑
Linaria非常に小さい中程度良好ゼロランタイム

Emotion は、特に軽量性学習コストの低さで優れており、初心者にも取り組みやすいライブラリです。

具体例

環境構築とインストール

それでは、実際に Emotion を使った開発を始めてみましょう。

Step 1: 新しい Next.js プロジェクトの作成

bash# Next.jsプロジェクトを作成
npx create-next-app@latest emotion-tutorial --typescript --tailwind --eslint

# プロジェクトディレクトリに移動
cd emotion-tutorial

Step 2: Emotion のインストール

bash# Emotionのコアパッケージをインストール
yarn add @emotion/react @emotion/styled

# Next.js用の設定パッケージも追加
yarn add @emotion/babel-plugin

Step 3: Next.js での Emotion 設定

Next.js で Emotion を使用するために、.babelrcファイルを作成します:

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

よくあるエラーと解決法

この段階で発生する可能性のあるエラーとその解決法をご紹介します:

bash# エラー例1: babel-plugin-macrosが見つからない
Error: Cannot find module 'babel-plugin-macros'

# 解決法
yarn add babel-plugin-macros --dev
bash# エラー例2: TypeScriptの型エラー
Property 'css' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'

# 解決法: tsconfig.jsonにjsxImportSourceを追加
{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "@emotion/react"
  }
}

基本的な使い方(styled component)

Emotion の基本的な使い方を、実際のコンポーネントを作成しながら学んでいきましょう。

最初の Styled Component

typescript// components/Button.tsx
import styled from '@emotion/styled';

// 基本的なボタンコンポーネント
const StyledButton = styled.button`
  background: #007bff;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  transition: background 0.2s ease;

  &:hover {
    background: #0056b3;
  }

  &:active {
    transform: translateY(1px);
  }
`;

export const Button = ({ children, ...props }) => {
  return <StyledButton {...props}>{children}</StyledButton>;
};

この例では、通常のbutton要素にスタイルを適用したStyledButtonを作成しています。CSS の書き方は従来の CSS とほとんど同じですが、コンポーネント内にスコープが限定されます。

CSS Prop を使った書き方

typescript// components/Card.tsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const cardStyles = css`
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  padding: 20px;
  margin: 10px;
  transition: transform 0.2s ease;

  &:hover {
    transform: translateY(-2px);
  }
`;

export const Card = ({ children }) => {
  return <div css={cardStyles}>{children}</div>;
};

CSS Prop を使用することで、より直感的にスタイルを適用できます。

props を使った動的スタイリング

Emotion の真の力は、props に基づいた動的なスタイリングにあります。

条件付きスタイリング

typescript// components/Alert.tsx
import styled from '@emotion/styled';

// 型定義
interface AlertProps {
  variant: 'success' | 'warning' | 'error' | 'info';
  children: React.ReactNode;
}

const StyledAlert = styled.div<AlertProps>`
  padding: 16px;
  border-radius: 4px;
  margin: 10px 0;
  border-left: 4px solid;

  ${({ variant }) => {
    switch (variant) {
      case 'success':
        return `
          background: #d4edda;
          border-color: #28a745;
          color: #155724;
        `;
      case 'warning':
        return `
          background: #fff3cd;
          border-color: #ffc107;
          color: #856404;
        `;
      case 'error':
        return `
          background: #f8d7da;
          border-color: #dc3545;
          color: #721c24;
        `;
      case 'info':
        return `
          background: #d1ecf1;
          border-color: #17a2b8;
          color: #0c5460;
        `;
      default:
        return '';
    }
  }}
`;

export const Alert: React.FC<AlertProps> = ({
  variant,
  children,
}) => {
  return (
    <StyledAlert variant={variant}>{children}</StyledAlert>
  );
};

使用例

typescript// pages/index.tsx
import { Alert } from '../components/Alert';

export default function Home() {
  return (
    <div>
      <Alert variant='success'>
        操作が正常に完了しました!
      </Alert>
      <Alert variant='error'>
        エラーが発生しました。再試行してください。
      </Alert>
    </div>
  );
}

テーマ機能の活用

より高度な使い方として、テーマ機能を使った統一されたデザインシステムの構築も可能です:

typescript// theme/theme.ts
export const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    success: '#28a745',
    danger: '#dc3545',
    warning: '#ffc107',
    info: '#17a2b8',
  },
  spacing: {
    xs: '4px',
    sm: '8px',
    md: '16px',
    lg: '24px',
    xl: '32px',
  },
  fontSize: {
    sm: '14px',
    md: '16px',
    lg: '18px',
    xl: '24px',
  },
};

// TypeScript用の型定義
export type Theme = typeof theme;
typescript// components/ThemedButton.tsx
import styled from '@emotion/styled';
import { Theme } from '../theme/theme';

interface ThemedButtonProps {
  variant?: 'primary' | 'secondary';
  size?: 'sm' | 'md' | 'lg';
}

const ThemedButton = styled.button<ThemedButtonProps>`
  background: ${({
    theme,
    variant = 'primary',
  }: {
    theme: Theme;
    variant?: 'primary' | 'secondary';
  }) => theme.colors[variant]};
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: opacity 0.2s ease;

  padding: ${({
    theme,
    size = 'md',
  }: {
    theme: Theme;
    size?: 'sm' | 'md' | 'lg';
  }) => {
    switch (size) {
      case 'sm':
        return `${theme.spacing.sm} ${theme.spacing.md}`;
      case 'lg':
        return `${theme.spacing.lg} ${theme.spacing.xl}`;
      default:
        return `${theme.spacing.md} ${theme.spacing.lg}`;
    }
  }};

  font-size: ${({
    theme,
    size = 'md',
  }: {
    theme: Theme;
    size?: 'sm' | 'md' | 'lg';
  }) => theme.fontSize[size]};

  &:hover {
    opacity: 0.8;
  }
`;

export { ThemedButton };

実際の使用例とエラーハンドリング

実際の開発では、以下のようなエラーが発生することがあります:

typescript// よくあるエラー例
const BrokenComponent = styled.div`
  background: ${(props) =>
    props.color}; // undefinedになる可能性
  padding: ${(props) => props.size}px; // 型エラーの可能性
`;

// 改善版
interface ComponentProps {
  color?: string;
  size?: number;
}

const SafeComponent = styled.div<ComponentProps>`
  background: ${({ color = '#fff' }) => color};
  padding: ${({ size = 16 }) => size}px;
`;

まとめ

Emotion の導入メリット

本記事では、Emotion の基本的な使い方から実践的な活用方法まで、3 分で始められる範囲をご紹介しました。

主要なメリットの再確認

  1. 学習コストの低さ 従来の CSS の知識をそのまま活用できるため、新しい概念を覚える必要が最小限です。

  2. 型安全性 TypeScript との組み合わせにより、スタイルプロパティの補完やエラーチェックが利用できます。

  3. 優れた開発体験 Hot Reload によるリアルタイムな変更確認や、VSCode での豊富な拡張機能が利用できます。

  4. パフォーマンス 必要なスタイルのみがバンドルされ、実行時のパフォーマンスも優秀です。

次のステップへの案内

Emotion の基本を理解したら、以下のステップに進むことをお勧めします:

ステップ内容所要時間
1アニメーション機能の習得30 分
2テーマシステムの構築1 時間
3パフォーマンス最適化1 時間
4テストの実装1 時間

実際のプロジェクトでの活用

実際の開発では、以下のような場面で Emotion の力を発揮できます:

  • デザインシステムの構築 一貫した UI/UX を提供するためのコンポーネントライブラリ作成

  • レスポンシブデザイン メディアクエリを使った柔軟なレイアウト調整

  • 動的テーマ切り替え ダークモード対応やユーザーカスタマイズ機能

今回学んだ基本的な使い方を基に、ぜひ実際のプロジェクトで Emotion を活用してみてください。きっと、スタイリングの新しい可能性を発見できるでしょう。

関連リンク