Reactコンポーネントのスタイルは普通のCSSで付けます。className propで要素にクラスを付けてスタイルシートにルールを書き、JavaScriptから来る値には style propを使い、プロジェクトが大きくなったらCSS ModulesやTailwindを使います。次の例は、このページで動かせるようにCSSを <style> 要素の中に書いています。プロジェクトでは .css ファイルに置くところです。
.btn のルールの #2563eb を #16a34a に変えると、ボタンが緑になります。セレクタ、メディアクエリ、ホバー状態、アニメーションなど、CSSについて知っていることはすべてそのまま使えます。
classではなくclassName
JSXはJavaScriptにコンパイルされ、そこでは class が予約語なので、ReactはDOM自身がそのプロパティに使う名前である className(element.className)を使います。ブラウザが受け取るのは普通の class 属性です。同じように、ラベルでは for の代わりに htmlFor を使います。React 19は開発中に警告を出しつつ class も受け付けますが、正しい名前は className で、どのコードベースもこちらを使います。
className は文字列を受け取るので、複数のクラスはスペースで区切ります:className="btn btn-primary"。値はJavaScriptなので、計算して作ることもできます。
条件付きのクラス
ある場合にだけクラスを付けるには、三項演算子かテンプレートリテラルで文字列を組み立てます。選択中のタブを強調するタブバーが典型的な例です。
Lessonsをクリックすると下線が移動します。今は、そのボタンの文字列だけに active が含まれるからです。Reviewsには disabled クラスと disabled 属性が付くので、灰色になりクリックを無視します。テンプレートリテラルは条件が偽のときに余分なスペースを残しますが、ブラウザはそれを無視します。条件付きのクラスが多いコンポーネントでは、配列のほうがすっきりします。
const classes = ['tab', isActive && 'active', isDisabled && 'disabled']
.filter(Boolean)
.join(' ');
多くのプロジェクトでは、同じ目的に小さな clsx パッケージを使います:clsx('tab', { active: isActive, disabled: isDisabled })。
style prop
style propは文字列ではなくJavaScriptのオブジェクトを受け取ります。二重の波かっこになるのはそのためで、外側の組がJavaScriptの式を開き、内側の組がオブジェクトリテラルです。プロパティ名はキャメルケース(backgroundColor、fontSize、borderRadius)で、値は文字列か数値です。長さのプロパティの数値はピクセルになるので、padding: 12 は 12px という意味です。
インラインスタイルが真価を発揮するのは、プログレスバーの幅のように、値がレンダリング時に計算されるときです。
バーがいっぱいになるまでAdd 10%をクリックすると、緑に変わります。width: percent + '%' に注目してください。単なる数値はピクセルを意味するので、px 以外の単位は文字列に入れます。単位のないプロパティは、opacity: 0.8、zIndex: 2、flexGrow: 1、lineHeight: 1.5(ピクセルではなく倍率です)のように普通の数値のままです。
インラインスタイルには限界があります。:hover、:focus、メディアクエリ、キーフレームアニメーションは書けず、各要素がルールのコピーを持つことになります。動的な値に使い、それ以外はCSSに置いてください。
propsからCSS変数を渡す
ちょうどよい中間の方法は、style でCSSカスタムプロパティを設定し、それをスタイルシートで使うことです。ホバー状態も含めてルールはCSSに残り、値だけがJavaScriptから来ます。
新しい色を選ぶと両方のボタンがそれに従い、ホバー時の塗りつぶしも普通のCSSなので動き続けます。カスタムプロパティの名前は style オブジェクトの中でもハイフンを保つので、引用符で囲みます:{ '--accent': accent }。
CSSファイル
実際のプロジェクトでは、スタイルを .css ファイルに書き、それを必要とするコンポーネントからインポートします。Viteならインポートだけで十分です。ビルドツールが import './Button.css' を見つけ、開発中はそのスタイルシートをページに追加し、本番用にはCSSファイルにバンドルします。使うべきエクスポートはなく、インポートはその副作用のためだけにあります。
// Button.jsx
import './Button.css';
export default function Button({ children }) {
return <button className="button">{children}</button>;
}
/* Button.css */
.button {
background: #2563eb;
color: white;
border-radius: 6px;
}
注意点は、インポートしたスタイルシートがすべてグローバルであることです。Button.css の .button ルールは、アプリのどこであれそのクラスを持つすべての要素にスタイルを付け、どちらも .title を定義する2つのファイルは互いに上書きし合います。チームではBEM(.card__title)のような命名規則か、CSS Modulesでこれに対処します。
CSS Modules
.module.css で終わるファイルはCSS Moduleです。それをインポートすると、書いた各クラス名を一意に生成された名前に対応させるオブジェクトが手に入るので、クラスは使った場所にしか適用されません。ViteとNext.jsは設定なしでこれをサポートしています。
// Card.jsx
import styles from './Card.module.css';
export default function Card({ title, children, featured }) {
return (
<section className={featured ? `${styles.card} ${styles.featured}` : styles.card}>
<h3 className={styles.title}>{title}</h3>
{children}
</section>
);
}
/* Card.module.css */
.card { border: 1px solid #ddd; border-radius: 10px; padding: 16px; }
.featured { border-color: #2563eb; }
.title { margin: 0; }
ブラウザでは、クラスは _card_1x2ab_1 のような名前になります。別のコンポーネントが独自の .title を定義しても、2つが衝突することはありません。ホバーのルールやメディアクエリも含めて普通のCSSを書けるので、CSS Modulesはコンポーネントのスタイルによく使われる標準的な選択肢です。
ReactでTailwind CSSを使う
Tailwindは別のアプローチを取ります。ルールを書く代わりに、小さなユーティリティクラスを className に直接組み合わせます。px-4 は横方向の余白を、rounded-lg は角の丸みを設定し、hover:bg-blue-700 はホバー時に背景を変えます。Tailwindはファイルをスキャンし、使われているクラスのCSSだけを生成します。
ViteのReactアプリ(Tailwind v4)での設定は3つの手順です。
npm install tailwindcss @tailwindcss/vite
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [react(), tailwindcss()],
});
/* src/index.css, imported from main.jsx */
@import "tailwindcss";
あとはクラスでコンポーネントにスタイルを付けます。
function UpgradeCard({ active }) {
return (
<div className="max-w-xs rounded-xl border border-gray-200 p-4">
<h3 className="mb-2 text-lg font-semibold">Pro plan</h3>
<button
className={`rounded-md px-4 py-2 text-white ${active ? 'bg-green-600' : 'bg-blue-600 hover:bg-blue-700'}`}
>
{active ? 'Active' : 'Upgrade'}
</button>
</div>
);
}
このページのエディタではTailwindが使えないので、静的なコードとして示しています。条件付きのクラスは上のタブの例とまったく同じように動きますが、各クラス名は省略せずに書いてください。Tailwindはソースのテキストをスキャンしてクラスを見つけるので、`bg-${color}-600` のように実行時に組み立てた名前は生成されません。
使い分け
- 素のCSSファイルは、小さなアプリやグローバルなスタイル(リセット、フォント、ページのレイアウト)に向いています。アプリが大きくなったらクラス名の衝突に注意してください。
- CSS Modulesは、ほとんどのコンポーネントのスタイルに向いています。普通のCSSで、自動的にスコープが付き、追加のライブラリもいりません。
- Tailwindは、マークアップの中でスタイルを付けたい、余白や色に共通のデザインスケールを使いたいチームに向いています。
stylepropは、幅、位置、ユーザーが選んだ色など、レンダリング時に計算する値のためのものです。CSS変数と組み合わせれば、ホバーやメディアのルールをCSSに残せます。
組み合わせても構いません。典型的なアプリは、グローバルなスタイルシートを1つ持ち、コンポーネントにはCSS ModulesかTailwindを使い、動的な値にはいくつかのインラインスタイルを使います。styled-componentsやEmotionのようなCSS-in-JSライブラリも選択肢の1つで、古いコードベースで目にするでしょう。これらすべてが動くViteプロジェクトの始め方は、Reactのインストールのページで紹介しています。
よくある質問
Reactがclassではなく、classNameを使うのはなぜですか?
JSXはJavaScriptにコンパイルされ、そこでは class が予約語だからです。ReactはDOMのプロパティ名である className を使います。素のJavaScriptでも element.className と呼ばれる名前です。HTMLでは普通の class 属性になります。
Reactでインラインスタイルはどう書きますか?
style propにキャメルケースのプロパティ名を持つオブジェクトを渡します:style={{ backgroundColor: 'teal', fontSize: 18 }}。外側の波かっこがJavaScriptの式を開き、内側の波かっこがオブジェクトです。長さを取るプロパティでは、数値はピクセルになります。
Reactで条件に応じてクラスを付けるには?
JavaScriptで文字列を組み立てます:className={isActive ? 'tab active' : 'tab'} や、className={`tab ${isActive ? 'active' : ''}`} のようなテンプレートリテラルです。条件が多いときは、clsx パッケージのような小さな補助関数を使うと読みやすく保てます。
ReactのCSSファイルはグローバルですか?
はい。import './App.css' はスタイルシートをページ全体に追加するので、そのクラス名はほかのファイルと衝突することがあります。CSS Modules(App.module.css)は各クラスを一意の名前に変えるので、インポートした場所にだけ適用されます。
ReactでTailwindを使うには?
Viteのプロジェクトで tailwindcss と @tailwindcss/vite をインストールし、vite.config.js にプラグインを追加して、メインのCSSファイルの先頭に @import "tailwindcss"; を書きます。その後、className="px-4 py-2 rounded bg-blue-600 text-white" のように className にユーティリティクラスを書きます。