React でコンポーネントを書き始めると、まず戸惑うのがスタイルの当て方です。HTML と同じつもりで class="card" と書いても警告が出ますし、style="color: red" のように文字列を渡すとエラーになります。React の JSX では、クラスの指定は className、インラインスタイルはオブジェクトで style に渡す、という決まりがあるためです。この記事では、className と style の正しい書き方、プロパティ名がキャメルケースになる理由、条件によってクラスを切り替える方法、そして CSS 変数の指定までを解説します。対象は React を書き始めたばかりの初心者〜中級者の方です。
目次
class ではなく className を使う
JSX は HTML によく似ていますが、実体は JavaScript です。class は JavaScript の予約語(クラス構文で使う語)なので属性名として使えず、React では DOM のプロパティ名に合わせた className を使います。同じ理由で for 属性も htmlFor になります。
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重になります。外側が式の開始、内側がオブジェクトです。
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: 16 は padding: 16px になるということです。ただし、もともと単位を持たないプロパティ(lineHeight、zIndex、opacity、flex、fontWeight など)は数値のまま扱われます。% や 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 には文字列を渡すだけなので、条件による切り替えはただの文字列操作です。テンプレートリテラルと三項演算子を組み合わせるのが、追加ライブラリのいらない基本形です。
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つと増えてくると文字列の連結が読みにくくなるので、実務では clsx や classnames といった小さなライブラリを使うことが多いです。clsx('tab', { 'tab--active': isActive }) のように書け、false や undefined の項目は自動で無視されます。ライブラリを増やしたくない場合は、上の join(' ') 方式で十分実用になります。
className と style の使い分け
基本は className で CSS ファイル側にスタイルを書き、style は限定的に使うのがおすすめです。インラインスタイルは詳細度が非常に高く(!important 以外で上書きできません)、擬似クラス(:hover)や擬似要素(::before)、メディアクエリも書けないためです。
逆に style が向くのは、実行時にしか決まらない値を渡すときです。進捗バーの幅、ユーザーが選んだテーマ色、ドラッグ中の要素の座標などは CSS ファイルに書けないので、インラインスタイルか、後述の CSS 変数で渡します。
function ProgressBar({ percent }) {
return (
<div className="progress">
{/* 見た目のルールは CSS、変化する幅だけ style で渡す */}
<div className="progress__bar" style={{ width: `${percent}%` }} />
</div>
);
}
CSS 変数を style から渡す
インラインスタイルでは擬似クラスが書けない、という制約は CSS 変数(カスタムプロパティ)で回避できます。style オブジェクトのキーに -- で始まる名前を書くと、そのままカスタムプロパティとして出力されます。CSS 側でその変数を参照すれば、:hover の中でも動的な値を使えます。
function ThemeButton({ color, children }) {
// カスタムプロパティはキャメルケースにせず、そのまま書く
return (
<button className="theme-button" style={{ '--accent': color }}>
{children}
</button>
);
}
.theme-button {
border: 1px solid var(--accent);
color: var(--accent);
background: transparent;
}
/* インラインでは書けない :hover でも、変数なら使える */
.theme-button:hover {
background: var(--accent);
color: #fff;
}
なお TypeScript(.tsx)で書く場合、--accent は CSSProperties に存在しないキーなので型エラーになります。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 を使います(for は htmlFor)。インラインスタイルは文字列ではなくオブジェクトで渡し、style={{ backgroundColor: 'red' }} のように波括弧が2重になる点と、プロパティ名がキャメルケースになる点を押さえましょう。数値を渡すと多くのプロパティで自動的に px が付き、lineHeight や zIndex のような単位を持たないプロパティはそのまま扱われます。条件によるクラスの切り替えは三項演算子や配列の join(' ') で行い、複雑になるなら clsx のようなライブラリを検討してください。擬似クラスやメディアクエリが書けないインラインスタイルは、進捗バーの幅など実行時にしか決まらない値に絞って使い、それ以外は CSS ファイル側に寄せるのが扱いやすい構成です。動的な値をホバー時にも使いたいときは、style から CSS 変数を渡す方法が便利です。