Next.js + Tailwind CSS ベストプラクティス:v4対応の完全ガイド (2025年版)

VS Code でボタンコンポーネントの編集中、ふとクラス名を数えてみました。……23個。bg-blue-500 から始まって dark:hover:bg-blue-800 まで、呪文のように横に長く伸びて画面からはみ出しています。通りがかった同僚に「何その暗号?」と言われる始末。
Tailwind CSS を使い始めて2年、開発スピードは確かに上がりました。でも、コピペで増殖するクラス名、変更時の「全ファイル検索置換」地獄、そしてなんだかんだで肥大化する CSS バンドルサイズ……。「本当にこれでいいんだっけ?」と自問自答する日々でした。
そして2025年、Tailwind CSS v4 の登場と Next.js 15 の進化により、状況は一変しました。設定ファイル(tailwind.config.js)が不要になり、CSS 変数ベースのテーマ設定が可能になり、ビルド速度が爆速になりました。
しかし、v4 になっても「書き方」が悪いとプロジェクトはカオスになります。
この記事では、私が実際のプロジェクトで実践し、効果を上げた「メンテナンス可能な Tailwind CSS の書き方」と、v4 時代の新しい最適化手法(CSSサイズ90%削減など)を余すことなく紹介します。
Tailwind CSS v4 + Next.js 15:何が変わった?
設定ファイルが消えた?
v4 の最大の変化は「Zero-Config(ゼロ設定)」思想です。
これまで必須だった tailwind.config.js が、なんとオプショナル(任意)になりました。「え、じゃあテーマカラーはどう設定するの?」と思いますよね。
答えは CSS です。global.css に直接変数を書きます。
@theme {
--color-primary: #3b82f6;
--color-secondary: #8b5cf6;
--font-sans: 'Inter', sans-serif;
}これだけで、text-primary や bg-secondary というクラスが使えるようになります。
この変更の素晴らしい点は、ホットリロードが爆速なことです。今まで tailwind.config.js を書き換えると開発サーバーの再起動が必要でしたが、CSS 変数なら一瞬で反映されます。デザイナーとの調整も、ブラウザの DevTools で変数をいじるだけで確認できるので非常にスムーズです。
圧倒的な速度向上
Rust で書き直された新エンジンのおかげで、ビルド速度が劇的に向上しました。私のプロジェクトでの実測値です:
「クラス名長すぎ問題」の解決策:CVA パターン
よくある「悪い Tailwind」の例:
// ❌ コピペで増殖する長いクラス名
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-200">
保存
</button>@apply を使って CSS ファイルに逃がすのも一つの手ですが、Tailwind 作者はこれを推奨していません(CSS バンドルが肥大化するため)。
正解は、CVA (Class Variance Authority) を使ったコンポーネント化です。
import { cva, type VariantProps } from 'class-variance-authority'
// バリエーションを定義
const buttonStyles = cva(
'font-bold rounded-lg transition duration-200', // 共通スタイル
{
variants: {
variant: {
primary: 'bg-blue-500 hover:bg-blue-700 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-800',
danger: 'bg-red-500 hover:bg-red-700 text-white'
},
size: {
sm: 'py-1 px-3 text-sm',
md: 'py-2 px-4',
lg: 'py-3 px-6 text-lg'
}
},
defaultVariants: {
variant: 'primary',
size: 'md'
}
}
)
export function Button({ variant, size, className, ...props }: any) {
return (
<button className={buttonStyles({ variant, size })} {...props} />
)
}使うときはこうなります:
// ✅ スッキリ!
<Button variant="primary">保存</Button>
<Button variant="danger" size="lg">削除</Button>Shadcn UI などモダンなライブラリはすべてこのパターンを採用しています。型安全性も担保されるので、variant="super-danger" のような存在しないスタイルを指定するとエディタがエラーを出してくれます。
デザインシステムの構築:CSS 変数が鍵
プロジェクト全体で色やサイズを統一するには、デザインシステムが必要です。
v4 では global.css に定義します。
@import 'tailwindcss';
@theme {
/* ブランドカラー */
--color-brand-primary: #3b82f6;
--color-brand-secondary: #8b5cf6;
/* 状態カラー */
--color-success: #10b981;
--color-error: #ef4444;
/* スペーシング(8pxグリッド) */
--spacing-unit: 0.5rem;
}これの良い点は、ランタイムでのテーマ切り替が容易なことです。
例えば、「ユーザーがテーマカラーを選べる機能」を作りたい場合、CSS 変数の値を JS で書き換えるだけで、サイト全体の色が一瞬で変わります。再ビルドは不要です。
v4 時代のダークモード実装
v3 までは tailwind.config.js に darkMode: 'class' を書く必要がありましたが、v4 ではデフォルトでクラスベースのダークモードが有効になっています。
実装は next-themes を使うのが鉄板です。
npm install next-themes// app/providers.tsx
'use client'
import { ThemeProvider } from 'next-themes'
export function Providers({ children }) {
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
)
}そして、CSS 変数もダークモードに対応させます。
@theme {
--color-bg-primary: #ffffff;
--color-text-primary: #111827;
}
/* .dark クラスがついた時の上書き */
.dark {
--color-bg-primary: #111827;
--color-text-primary: #f9fafb;
}これで、bg-[--color-bg-primary] と書くだけで、ライト・ダーク両対応の背景色になります。dark:bg-black のようにプレフィックスを書く必要すらありません。
500KB → 50KB:究極のパフォーマンス最適化
Tailwind はデフォルトでも十分軽量ですが、気をつけないとゴミが溜まります。
1. スキャン対象を絞る(v3 ユーザー向け)
v4 は自動スキャンですが、v3 を使っている場合は content 配列を正確に指定しないと、ビルド時間が伸びたり、不要なクラスが含まれたりします。
// ❌ 範囲が広すぎる
content: ['./**/*.{js,ts,jsx,tsx}']
// ✅ 必要な場所だけ
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx}',
]2. 動的クラス名の罠
これが一番よくあるミスです。
// ❌ 絶対にやってはいけない
const color = 'red'
<div className={`bg-${color}-500`}></div>Tailwind はビルド時に静的解析を行い、ソースコードに存在するクラス名だけを CSS に出力します。bg-${color}-500 という文字列はスキャン時に bg-red-500 として認識されないため、このスタイルは消えます(Purge されます)。
正しい書き方:
// ✅ 完全なクラス名を書く
const colorMap = {
red: 'bg-red-500',
blue: 'bg-blue-500',
}
<div className={colorMap[color]}></div>または、どうしても動的にしたい場合は safelist に追加しますが、サイズが増えるので最後の手段にしてください。
3. バンドル分析
@next/bundle-analyzer を使って、定期的に CSS サイズをチェックしましょう。
「アイコンライブラリを丸ごとインポートしていた」「使っていない UI キットのスタイルが残っていた」などの無駄がすぐに見つかります。
まとめ:v4 への移行は必要?
v4 は素晴らしいですが、既存の大規模プロジェクトを無理に移行する必要はありません。v3 も十分安定しています。
しかし、新規プロジェクトなら迷わず v4 + Next.js 15 で始めるべきです。開発体験(DX)の向上は凄まじいものがあります。
まずは小さいところから始めましょう。
- よく使う UI(ボタン、カード)を
cvaでコンポーネント化する。 global.cssでデザインシステムの変数を定義する。- 動的クラス名生成をやめる。
これだけでも、あなたのコードベースは劇的にきれいになります。
Tailwind CSS v4 + Next.js 最適化フロー
クラス名管理からバンドルサイズ削減までの実装ステップ
⏱️ Estimated time: 1 hr
- 1
Step1: CVA コンポーネントの作成
`class-variance-authority` をインストールし、ボタンコンポーネントを作成します。`variant`(primary, secondary)と `size` プロパティを定義し、利用側からクラス名を隠蔽します。 - 2
Step2: CSS 変数によるテーマ定義
`app/globals.css` の `@theme` ブロック内に、ブランドカラーやフォント設定を CSS 変数として定義します。これにより `tailwind.config.js` なしでカスタマイズが可能になります。 - 3
Step3: ダークモード設定
`next-themes` を導入し、ルートレイアウトで `ThemeProvider` をラップします。CSS 変数を使って `.dark` クラス時の色定義を上書きすることで、シームレスな切り替えを実現します。 - 4
Step4: 動的クラス名の排除
コード内を検索し、文字列連結(`Use template literals`)でクラス名を生成している箇所を特定。オブジェクトマップ(`const map = { key: 'class-name' }`)方式に書き換えて、Purge 漏れを防ぎます。
FAQ
v4 はまだベータ版ですか?本番で使えますか?
@apply は使ってもいいですか?
Shadcn UI は v4 に対応していますか?
CSS Modules との併用は可能ですか?
4 min read · 公開日: 2025年12月20日 · 更新日: 2026年1月22日
関連記事
Next.js ファイルアップロード完全ガイド:S3/Qiniu Cloud 署名付き URL 直接アップロード実践

Next.js ファイルアップロード完全ガイド:S3/Qiniu Cloud 署名付き URL 直接アップロード実践
Next.js Eコマース実践:カートと Stripe 決済の完全実装ガイド

Next.js Eコマース実践:カートと Stripe 決済の完全実装ガイド
Next.js ユニットテスト実践:Jest + React Testing Library 完全設定ガイド


コメント
GitHubアカウントでログインしてコメントできます