Reactでクラス名が効かない|classNameの正しい使い方

Reactでクラス名が効かない原因と解決方法を解説。classNameの正しい使い方や動的クラス名の設定方法を初心者向けに紹介します。

Learning Next 運営
25 分で読めます

みなさん、ReactでCSSクラス名を指定したときに困ったことはありませんか?

「スタイルが適用されない」「クラス名が効かない」という経験をした方も多いと思います。 実は、これにはちゃんとした理由があるんです。

ReactではHTMLのclass属性ではなく、className属性を使用する必要があります。 また、動的にクラス名を変更する場合も、特有の書き方があります。

この記事では、Reactでクラス名が効かない原因と、classNameの正しい使い方を詳しく解説します。 基本的な使い方から実践的なテクニックまで、わかりやすく説明していきますね。

ReactでclassNameを使う理由

まず、なぜReactでclassNameを使う必要があるのか理解しましょう。 この理由がわかると、以降の内容もすんなり理解できます。

HTMLのclass属性との違い

HTMLでは、こんな風にクラス名を指定しますよね。

<!-- HTMLの書き方 -->
<div class="container">
  <h1 class="title">Hello World</h1>
</div>

でも、Reactでは違います。

// ❌ HTMLの書き方(Reactでは動作しない)
<div class="container">
  <h1 class="title">Hello World</h1>
</div>

// ✅ Reactでの正しい書き方
<div className="container">
  <h1 className="title">Hello World</h1>
</div>

なぜこうなるかというと、ReactではJSXを使用するからです。 JSXはJavaScriptの中に書かれているので、JavaScriptの予約語であるclassと競合してしまうんです。

そのため、classNameという属性名を使うことになりました。

classを使った場合の警告

もし間違ってclassを使ってしまうと、どうなるでしょうか?

// ❌ class を使用した場合
function MyComponent() {
  return <div class="container">Content</div>;
}

この場合、ブラウザのコンソールにこんな警告が表示されます。

Warning: Invalid DOM property `class`. Did you mean `className`?

親切に「classNameを使いましょう」と教えてくれるんですね。 でも、警告が出るのは良くないので、最初からclassNameを使いましょう。

基本的なclassNameの使い方

それでは、classNameの基本的な使用方法を見ていきましょう。 段階的に説明していくので、安心してください。

単一クラスの指定

まず、シンプルなクラス名の指定方法です。

function Button() {
  return (
    <button className="btn">
      クリック
    </button>
  );
}

このボタンに対応するCSSは、こんな感じです。

.btn {
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

単一のクラス名を指定する場合は、文字列として直接指定します。 とてもシンプルですね。

複数クラスの指定

複数のクラス名を同時に指定したい場合は、どうすればいいでしょうか?

function AlertBox() {
  return (
    <div className="alert alert-warning">
      <p>警告メッセージです</p>
    </div>
  );
}

複数のクラス名を指定する場合は、スペース区切りで文字列内に記述します。 HTMLと同じ方法ですね。

変数を使ったクラス名

クラス名を動的に変更したい場合は、変数を使います。

function Card({ type }) {
  const cardClass = `card card-${type}`;
  
  return (
    <div className={cardClass}>
      <h3>カードタイトル</h3>
      <p>カードの内容</p>
    </div>
  );
}

// 使用例
<Card type="primary" />  // "card card-primary"
<Card type="secondary" /> // "card card-secondary"

この例では、typepropによって異なるクラス名を生成しています。 テンプレートリテラルを使って、動的にクラス名を組み立てるのがポイントです。

実際に使ってみると、以下のようになります。

  • <Card type="primary" />の場合:class="card card-primary"
  • <Card type="secondary" />の場合:class="card card-secondary"

便利ですね!

条件付きクラスの適用

条件に応じてクラス名を変更したい場面も多いです。 いくつかの方法を紹介しますね。

三項演算子を使った条件分岐

最も基本的な方法は、三項演算子を使う方法です。

function Button({ isPrimary, isDisabled }) {
  return (
    <button 
      className={
        isPrimary ? "btn btn-primary" : "btn btn-secondary"
      }
      disabled={isDisabled}
    >
      ボタン
    </button>
  );
}

isPrimaryがtrueならbtn btn-primary、falseならbtn btn-secondaryが適用されます。

もう少し複雑な条件も書けます。

function StatusBadge({ status, isUrgent }) {
  return (
    <span 
      className={
        `badge ${status === 'active' ? 'badge-success' : 'badge-warning'} ${
          isUrgent ? 'badge-urgent' : ''
        }`
      }
    >
      {status}
    </span>
  );
}

複数の条件を組み合わせていますが、少し読みにくいですね。 でも大丈夫です。もっと読みやすい方法もあります。

論理演算子を使った条件付き適用

特定の条件が真の場合にのみクラスを追加したい場合は、論理演算子が便利です。

function Modal({ isOpen, hasOverlay }) {
  return (
    <div 
      className={
        `modal ${isOpen ? 'modal-open' : ''} ${
          hasOverlay && 'modal-overlay'
        }`
      }
    >
      <div className="modal-content">
        <p>モーダルの内容</p>
      </div>
    </div>
  );
}

hasOverlay && 'modal-overlay'の部分がポイントです。 hasOverlayがtrueの場合にのみ、modal-overlayクラスが追加されます。

配列とjoinを使った方法

複雑な条件になってくると、配列を使った方法が読みやすくなります。

function NavItem({ isActive, isDisabled, hasIcon }) {
  const classes = [
    'nav-item',
    isActive && 'nav-item-active',
    isDisabled && 'nav-item-disabled',
    hasIcon && 'nav-item-with-icon'
  ].filter(Boolean).join(' ');

  return (
    <li className={classes}>
      <a href="#link">ナビゲーション項目</a>
    </li>
  );
}

この方法の良いところは、条件が一目でわかることです。

配列の各要素を見ると、以下のようになっています。

  • 'nav-item':常に適用
  • isActive && 'nav-item-active':アクティブな場合のみ適用
  • isDisabled && 'nav-item-disabled':無効な場合のみ適用
  • hasIcon && 'nav-item-with-icon':アイコンがある場合のみ適用

filter(Boolean)で、falseの値を除去してから、join(' ')でスペース区切りの文字列に変換しています。

classnamesライブラリの活用

さらに複雑な条件付きクラス管理には、classnamesライブラリがおすすめです。 これは、React開発でとても人気のライブラリなんです。

classnamesライブラリのインストール

まず、ライブラリをインストールしましょう。

npm install classnames

基本的な使用方法

classnamesを使うと、こんな風に書けます。

import classNames from 'classnames';

function Button({ type, size, isDisabled, isLoading }) {
  const buttonClasses = classNames({
    'btn': true,
    [`btn-${type}`]: type,
    [`btn-${size}`]: size,
    'btn-disabled': isDisabled,
    'btn-loading': isLoading
  });

  return (
    <button className={buttonClasses}>
      {isLoading ? 'Loading...' : 'ボタン'}
    </button>
  );
}

// 使用例
<Button type="primary" size="large" isLoading={true} />
// 結果: "btn btn-primary btn-large btn-loading"

オブジェクトの形式で条件を書けるので、とても読みやすくて管理しやすいです。

  • 'btn': true:常に適用
  • [btn-${type}]: type:typeが存在する場合のみ適用
  • 'btn-disabled': isDisabled:isDisabledがtrueの場合のみ適用

複数の記法の組み合わせ

classnamesでは、複数の記法を組み合わせることもできます。

import classNames from 'classnames';

function Card({ variant, isSelected, className, children }) {
  const cardClasses = classNames(
    'card',                    // 常に適用
    `card-${variant}`,         // 変数ベース
    {
      'card-selected': isSelected,  // 条件付き
      'card-hover': true            // 常に適用
    },
    className                  // 外部から渡されるクラス
  );

  return (
    <div className={cardClasses}>
      {children}
    </div>
  );
}

この例では、以下の形式を組み合わせています。

  • 文字列:'card'
  • テンプレートリテラル:card-${variant}
  • オブジェクト:{ 'card-selected': isSelected }
  • 外部からのクラス:className

とても柔軟にクラス管理ができるんです。

CSS Modulesでのクラス名管理

CSS Modulesを使っている場合の、クラス名管理方法も見てみましょう。 CSS Modulesは、CSSクラス名の衝突を防ぐ仕組みです。

CSS Modulesの基本的な使用

まず、CSS Modulesのファイルを作成します。

/* Button.module.css */
.button {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.primary {
  background-color: #007bff;
  color: white;
}

.secondary {
  background-color: #6c757d;
  color: white;
}

.large {
  font-size: 1.2rem;
  padding: 15px 30px;
}

そして、Reactコンポーネントでインポートして使います。

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

function Button({ variant = 'primary', size }) {
  return (
    <button 
      className={`${styles.button} ${styles[variant]} ${
        size === 'large' ? styles.large : ''
      }`}
    >
      ボタン
    </button>
  );
}

CSS Modulesでは、stylesオブジェクトを通してクラス名にアクセスします。 styles.buttonstyles[variant]のような形で使います。

CSS Modulesとclassnamesの組み合わせ

CSS Modulesとclassnamesライブラリを組み合わせると、さらに使いやすくなります。

import classNames from 'classnames';
import styles from './Card.module.css';

function Card({ variant, isActive, isDisabled, className }) {
  const cardClasses = classNames(
    styles.card,
    styles[variant],
    {
      [styles.active]: isActive,
      [styles.disabled]: isDisabled
    },
    className  // グローバルクラスも適用可能
  );

  return (
    <div className={cardClasses}>
      <div className={styles.content}>
        カードの内容
      </div>
    </div>
  );
}

この組み合わせにより、CSS Modulesの利点を活かしつつ、柔軟なクラス管理ができます。

よくある問題とトラブルシューティング

クラス名が効かない場合の、よくある問題と解決方法を紹介します。 これらを覚えておくと、問題が起きたときに素早く対処できますよ。

1. classとclassNameの混同

最も基本的な間違いは、HTMLのclassを使ってしまうことです。

// ❌ 間違い:classを使用
function WrongComponent() {
  return <div class="container">Content</div>;
}

// ✅ 正解:classNameを使用
function CorrectComponent() {
  return <div className="container">Content</div>;
}

この間違いをすると、コンソールに警告が表示されます。 また、スタイルも適用されません。

2. 動的クラス名の誤った構文

動的にクラス名を生成する際の、よくある間違いです。

// ❌ 間違い:波括弧が不適切
function WrongDynamic({ type }) {
  return <div className="card-{type}">Content</div>;
}

// ✅ 正解:テンプレートリテラルを使用
function CorrectDynamic({ type }) {
  return <div className={`card-${type}`}>Content</div>;
}

// または文字列結合
function CorrectDynamic2({ type }) {
  return <div className={"card-" + type}>Content</div>;
}

動的クラス名では、波括弧内でテンプレートリテラルや文字列結合を使います。 className="card-{type}"のように書いても、{type}が展開されません。

3. undefinedやnullの値

propsからクラス名を受け取る際に、undefinedやnullが含まれる場合があります。

// ❌ 問題:undefinedやnullが含まれる可能性
function ProblematicComponent({ additionalClass }) {
  return <div className={`base-class ${additionalClass}`}>Content</div>;
  // additionalClass が undefined の場合、"base-class undefined" になる
}

// ✅ 解決策:デフォルト値を使用
function SafeComponent({ additionalClass }) {
  return (
    <div className={`base-class ${additionalClass || ''}`}>
      Content
    </div>
  );
}

// または配列を使った方法
function SafeComponent2({ additionalClass }) {
  const classes = ['base-class'];
  if (additionalClass) {
    classes.push(additionalClass);
  }
  
  return <div className={classes.join(' ')}>Content</div>;
}

additionalClassがundefinedの場合、「base-class undefined」という文字列になってしまいます。 これを防ぐために、デフォルト値や条件分岐を使いましょう。

実践的な使用例

実際のアプリケーションでの、実践的な使用例を見てみましょう。 これらの例を参考に、実際のプロジェクトでも活用してみてください。

ナビゲーションメニュー

現在のページをハイライトするナビゲーションメニューです。

import classNames from 'classnames';

function NavigationMenu({ currentPath }) {
  const menuItems = [
    { path: '/', label: 'ホーム' },
    { path: '/about', label: 'About' },
    { path: '/contact', label: 'Contact' }
  ];

  return (
    <nav className="navigation">
      <ul className="nav-list">
        {menuItems.map(item => (
          <li key={item.path}>
            <a 
              href={item.path}
              className={classNames('nav-link', {
                'nav-link-active': currentPath === item.path
              })}
            >
              {item.label}
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}

この例では、currentPathitem.pathを比較して、現在のページのリンクにアクティブクラスを適用しています。

フォームの状態表示

エラー状態や必須項目の表示を行うフォームです。

function FormInput({ label, value, onChange, error, isRequired }) {
  const inputClasses = classNames('form-input', {
    'form-input-error': error,
    'form-input-required': isRequired
  });

  const labelClasses = classNames('form-label', {
    'form-label-required': isRequired
  });

  return (
    <div className="form-group">
      <label className={labelClasses}>
        {label}
        {isRequired && <span className="required-indicator">*</span>}
      </label>
      <input 
        type="text"
        value={value}
        onChange={onChange}
        className={inputClasses}
      />
      {error && (
        <span className="form-error-message">{error}</span>
      )}
    </div>
  );
}

この例では、以下の条件でクラスを適用しています。

  • errorがある場合:form-input-errorクラスを追加
  • isRequiredがtrueの場合:form-input-requiredform-label-requiredクラスを追加

レスポンシブカード

サイズやテーマに応じてスタイルが変わるカードコンポーネントです。

function ResponsiveCard({ 
  title, 
  content, 
  size = 'medium', 
  theme = 'light',
  hasImage 
}) {
  const cardClasses = classNames('card', {
    [`card-${size}`]: size,
    [`card-${theme}`]: theme,
    'card-with-image': hasImage
  });

  return (
    <div className={cardClasses}>
      {hasImage && (
        <div className="card-image">
          <img src="/placeholder.jpg" alt={title} />
        </div>
      )}
      <div className="card-body">
        <h3 className="card-title">{title}</h3>
        <p className="card-content">{content}</p>
      </div>
    </div>
  );
}

この例では、以下の条件でクラスを動的に適用しています。

  • sizeに応じて:card-smallcard-mediumcard-large
  • themeに応じて:card-lightcard-dark
  • hasImageがtrueの場合:card-with-image

とても柔軟なコンポーネントですね。

まとめ

Reactでのクラス名の正しい使い方について、詳しく解説しました。

重要なポイント

  • HTMLのclassではなくclassNameを使用する
  • 動的クラス名はテンプレートリテラルや文字列結合で作成
  • 条件付きクラスには三項演算子や論理演算子を活用
  • 複雑な条件管理にはclassnamesライブラリが便利
  • undefinedやnullの値を適切に処理する

実践のコツ

適切なクラス名の管理により、Reactアプリケーションのスタイリングが効率的になります。 また、保守性も大幅に向上するんです。

条件に応じた動的なスタイル変更も、正しい方法を使えば簡単に実装できます。 最初は複雑に感じるかもしれませんが、慣れてくるととても便利です。

次のステップ

今回紹介した方法を参考に、Reactでのクラス名管理をマスターしてください。 きっと、思い通りのスタイリングができるようになるはずです。

実際のプロジェクトで試してみると、その便利さがよくわかりますよ!

関連記事