Tailwind CSS で動的に組み立てたクラス名が反映されない
Tailwind の JIT スキャナはソース上の完全な文字列だけをクラス候補に拾う。bg-${color}-500 のような実行時補完は検出されず CSS に出力されない。
完全文字列を分岐で書く、safelist に登録する、arbitrary value で逃がす、のいずれかで解決する。
公開: 更新:
要約
Tailwind の JIT スキャナは ソースコード上の完全な文字列だけ をクラス候補として抽出する。
テンプレートリテラルで組み立てた `bg-${color}-500` のような文字列は検出されず、最終 CSS にクラスが含まれない。
完全文字列で書く・safelist に入れる・arbitrary value で CSS 変数を渡す、の 3 系統で解決する。
よくある原因
- テンプレートリテラル:
`text-${size}`のように一部だけ変数化している。
完全なtext-lgの連続文字列がソース中に出てこない。 - 三項演算子 / props 埋め込み:
`text-${isActive ? 'red' : 'blue'}-500`も同様で、完全なtext-red-500がコード上に現れない。
親から受け取った props を`bg-${prop}`で展開する書き方も同じ罠。 - 配列 / map 生成: 色配列をループで回してクラス名を組み立てるパターン。
実行時に生成される文字列はビルド時には存在しない。 - ビルド後の CSS に無い: 結果として最終 CSS に対象クラスが出力されておらず、ブラウザに当てるべき値が無い。
解決策
1. 完全文字列で書く(最も推奨)
// NG: スキャンで検出されない
<div className={`bg-${color}-500`}>...</div>
// OK: 完全文字列を map に並べる
const bgMap = {
red: "bg-red-500",
blue: "bg-blue-500",
} as const;
<div className={bgMap[color]}>...</div>公式の dynamic class names ガイド(新しいタブで開く) でも、変数を文字列補間で組み立てず 完全クラス名を分岐させる ことが第一の推奨と明記されている。
2. safelist に登録する
// tailwind.config.js
module.exports = {
content: ["./src/**/*.{ts,tsx}"],
safelist: [
"bg-red-500",
"bg-blue-500",
{ pattern: /bg-(red|blue|green)-(100|500|900)/ },
],
};外部データに依存して静的解析に乗せられない場合は safelist(新しいタブで開く) を使う。pattern で正規表現指定も可能で、グループ単位でまとめて登録できる。
3. arbitrary value で CSS 変数を渡す
<div
style={{ "--brand": color } as React.CSSProperties}
className="bg-[var(--brand)]"
>
...
</div>色やサイズが完全に実行時依存(テーマカラーの動的選択など)なら、CSS 変数を介して Tailwind の任意値構文 bg-[var(--brand)] で受け取るのが自然。
クラス名の組み立てを諦めて style 経由にする発想。
実行例
テンプレートリテラルで組み立てたクラス名はスキャナに拾われず、bg-red-500 の出力件数は 0 のまま「No utility classes were detected」の警告まで出る。
完全な文字列を map に並べ直すと .bg-red-500 の宣言が CSS に現れ、safelist に pattern を登録した場合も bg-green-900 が出力される。
$ npx tailwindcss -i src/input.css -o out.css
Browserslist: caniuse-lite is outdated. Please run:
npx update-browserslist-db@latest
Why you should do it regularly: https://github.com/browserslist/update-db#readme
Rebuilding...
warn - No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.
warn - https://tailwindcss.com/docs/content-configuration
Done in 75ms.$ grep -c "bg-red-500" out.css
0
0 = 完全文字列 bg-red-500 がソースに存在しないため出力されない$ npx tailwindcss -i src/input.css -o out.css
Browserslist: caniuse-lite is outdated. Please run:
npx update-browserslist-db@latest
Why you should do it regularly: https://github.com/browserslist/update-db#readme
Rebuilding...
Done in 97ms.$ grep -A2 "\.bg-red-500" out.css
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1))$ npx tailwindcss -i src/input.css -o out.css
Browserslist: caniuse-lite is outdated. Please run:
npx update-browserslist-db@latest
Why you should do it regularly: https://github.com/browserslist/update-db#readme
Rebuilding...
Done in 149ms.$ grep -c "bg-green-900" out.css
22
1 以上 = safelist の pattern でまとめて出力されている— 2026-08-17 時点の出力
検証環境
- 検証日
- 実行環境
node:20
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。