1. ホーム
  2. React

【React】className と style の使い方|JSXでのクラス指定とインラインスタイルを解説

Share

React でコンポーネントを書き始めると、まず戸惑うのがスタイルの当て方です。HTML と同じつもりで class="card" と書いても警告が出ますし、style="color: red" のように文字列を渡すとエラーになります。React の JSX では、クラスの指定は className、インラインスタイルはオブジェクトで style に渡す、という決まりがあるためです。この記事では、classNamestyle の正しい書き方、プロパティ名がキャメルケースになる理由、条件によってクラスを切り替える方法、そして CSS 変数の指定までを解説します。対象は React を書き始めたばかりの初心者〜中級者の方です。

class ではなく className を使う

JSX は HTML によく似ていますが、実体は JavaScript です。class は JavaScript の予約語(クラス構文で使う語)なので属性名として使えず、React では DOM のプロパティ名に合わせた className を使います。同じ理由で for 属性も htmlFor になります。

Card.jsx
function Card() {
  return (
    <div className="card">
      {/* label の for は htmlFor になる */}
      <label className="card__label" htmlFor="title">
        タイトル
      </label>
      <input id="title" className="card__input" />
    </div>
  );
}

間違えて class と書いた場合、React はコンソールに「Invalid DOM property `class`. Did you mean `className`?」という警告を出します。開発中にこの警告を見かけたら、素直に className に直しましょう。複数のクラスを当てたいときは、HTML と同じくスペース区切りで className="card card--large" と書きます。

style はオブジェクトで渡す

インラインスタイルは、CSS の文字列ではなくオブジェクトで指定します。JSX の波括弧は「ここから JavaScript の式」という意味なので、オブジェクトリテラルを渡すときは style={{ ... }} と波括弧が2重になります。外側が式の開始、内側がオブジェクトです。

Badge.jsx
function Badge({ color }) {
  return (
    // 外側の {} は JS 式、内側の {} はオブジェクト
    <span
      style={{
        backgroundColor: color, // background-color → backgroundColor
        padding: '4px 8px',
        borderRadius: 4, // 数値を書くと自動で px が付く
        fontSize: '0.875rem',
      }}
    >
      新着
    </span>
  );
}

// スタイルを変数に切り出しても同じ
const titleStyle = { fontWeight: 'bold', lineHeight: 1.5 };

function Title() {
  return <h2 style={titleStyle}>見出し</h2>;
}

プロパティ名は background-color のようなハイフン区切りではなく、backgroundColor というキャメルケースで書きます。これは JavaScript から DOM の element.style を操作するときと同じ命名規則です。ハイフンを含むキーはそのままではオブジェクトのキーとして書けないため、この形になっています。ベンダープレフィックスは WebkitTransform のように先頭を大文字にし、-- で始まる CSS 変数だけは例外的にハイフンのまま書きます。

数値と単位のルール

値に数値を渡すと、React は多くのプロパティで自動的に px を補います。padding: 16padding: 16px になるということです。ただし、もともと単位を持たないプロパティ(lineHeightzIndexopacityflexfontWeight など)は数値のまま扱われます。%rem など px 以外の単位を使いたいときは、必ず文字列で書いてください。

書き方実際に適用される CSS
{ width: 200 }width: 200px
{ width: '50%' }width: 50%
{ lineHeight: 1.5 }line-height: 1.5(単位なしのまま)
{ zIndex: 10 }z-index: 10(単位なしのまま)
{ margin: '0 auto' }margin: 0 auto

条件によってクラスを切り替える

className には文字列を渡すだけなので、条件による切り替えはただの文字列操作です。テンプレートリテラルと三項演算子を組み合わせるのが、追加ライブラリのいらない基本形です。

TabButton.jsx
function TabButton({ label, isActive, isDisabled }) {
  // 三項演算子でクラスを足す
  const className = `tab ${isActive ? 'tab--active' : ''}`;

  return (
    <button className={className} disabled={isDisabled}>
      {label}
    </button>
  );
}

// 条件が増えるときは配列にして join でつなぐと読みやすい
function TabButton2({ label, isActive, isDisabled }) {
  const classNames = ['tab'];
  if (isActive) classNames.push('tab--active');
  if (isDisabled) classNames.push('tab--disabled');

  return <button className={classNames.join(' ')}>{label}</button>;
}

条件が3つ4つと増えてくると文字列の連結が読みにくくなるので、実務では clsxclassnames といった小さなライブラリを使うことが多いです。clsx('tab', { 'tab--active': isActive }) のように書け、falseundefined の項目は自動で無視されます。ライブラリを増やしたくない場合は、上の join(' ') 方式で十分実用になります。

className と style の使い分け

基本は className で CSS ファイル側にスタイルを書き、style は限定的に使うのがおすすめです。インラインスタイルは詳細度が非常に高く(!important 以外で上書きできません)、擬似クラス(:hover)や擬似要素(::before)、メディアクエリも書けないためです。

逆に style が向くのは、実行時にしか決まらない値を渡すときです。進捗バーの幅、ユーザーが選んだテーマ色、ドラッグ中の要素の座標などは CSS ファイルに書けないので、インラインスタイルか、後述の CSS 変数で渡します。

ProgressBar.jsx
function ProgressBar({ percent }) {
  return (
    <div className="progress">
      {/* 見た目のルールは CSS、変化する幅だけ style で渡す */}
      <div className="progress__bar" style={{ width: `${percent}%` }} />
    </div>
  );
}

CSS 変数を style から渡す

インラインスタイルでは擬似クラスが書けない、という制約は CSS 変数(カスタムプロパティ)で回避できます。style オブジェクトのキーに -- で始まる名前を書くと、そのままカスタムプロパティとして出力されます。CSS 側でその変数を参照すれば、:hover の中でも動的な値を使えます。

ThemeButton.jsx
function ThemeButton({ color, children }) {
  // カスタムプロパティはキャメルケースにせず、そのまま書く
  return (
    <button className="theme-button" style={{ '--accent': color }}>
      {children}
    </button>
  );
}
style.css
.theme-button {
  border: 1px solid var(--accent);
  color: var(--accent);
  background: transparent;
}

/* インラインでは書けない :hover でも、変数なら使える */
.theme-button:hover {
  background: var(--accent);
  color: #fff;
}

なお TypeScript(.tsx)で書く場合、--accentCSSProperties に存在しないキーなので型エラーになります。style={{ '--accent': color } as React.CSSProperties} のようにアサーションを付けるのが簡単な回避策です。

スタイルが当たらないときに確認すること

style に文字列を渡している

style="color: red"style={'color: red'} と書くと、React は「The `style` prop expects a mapping from style properties to values, not a string.」というエラーを出します。必ずオブジェクトで style={{ color: 'red' }} と渡してください。波括弧が1つしかないときは、オブジェクトになっていないサインです。

プロパティ名がハイフン区切りのまま

{ 'background-color': 'red' } のようにハイフンのまま書いてもエラーにはならず、単に無視されて何も起きないことがあります。適用されないスタイルがあるときは、キャメルケースになっているか(backgroundColor)をまず確認しましょう。例外は前述の CSS 変数だけです。

親から渡された className を無視している

自作コンポーネントに <Card className="mt-4" /> と渡しても、コンポーネントの中で受け取って DOM 要素に渡していなければ何も起きません。React は自作コンポーネントの props を自動で DOM に流し込まないためです。外側からの調整を許したいときは、function Card({ className }) のように受け取り、<div className={`card ${className ?? ''}`}> と自前のクラスに連結してあげる必要があります。

まとめ

JSX では class が予約語のため、クラスの指定には className を使います(forhtmlFor)。インラインスタイルは文字列ではなくオブジェクトで渡し、style={{ backgroundColor: 'red' }} のように波括弧が2重になる点と、プロパティ名がキャメルケースになる点を押さえましょう。数値を渡すと多くのプロパティで自動的に px が付き、lineHeightzIndex のような単位を持たないプロパティはそのまま扱われます。条件によるクラスの切り替えは三項演算子や配列の join(' ') で行い、複雑になるなら clsx のようなライブラリを検討してください。擬似クラスやメディアクエリが書けないインラインスタイルは、進捗バーの幅など実行時にしか決まらない値に絞って使い、それ以外は CSS ファイル側に寄せるのが扱いやすい構成です。動的な値をホバー時にも使いたいときは、style から CSS 変数を渡す方法が便利です。

参考ページ