言語を切り替える
テーマを切り替える

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-primarybg-secondary というクラスが使えるようになります。
この変更の素晴らしい点は、ホットリロードが爆速なことです。今まで tailwind.config.js を書き換えると開発サーバーの再起動が必要でしたが、CSS 変数なら一瞬で反映されます。デザイナーとの調整も、ブラウザの DevTools で変数をいじるだけで確認できるので非常にスムーズです。

圧倒的な速度向上

Rust で書き直された新エンジンのおかげで、ビルド速度が劇的に向上しました。私のプロジェクトでの実測値です:

500KB → 50KB
CSSバンドルサイズ
最適化により90%削減
5倍高速
ビルド速度
Rust製エンジンによる高速化
8秒 → 2秒
冷間起動時間
開発サーバーの起動待ち時間が大幅減

「クラス名長すぎ問題」の解決策: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.jsdarkMode: '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)の向上は凄まじいものがあります。

まずは小さいところから始めましょう。

  1. よく使う UI(ボタン、カード)を cva でコンポーネント化する。
  2. global.css でデザインシステムの変数を定義する。
  3. 動的クラス名生成をやめる。

これだけでも、あなたのコードベースは劇的にきれいになります。

Tailwind CSS v4 + Next.js 最適化フロー

クラス名管理からバンドルサイズ削減までの実装ステップ

⏱️ Estimated time: 1 hr

  1. 1

    Step1: CVA コンポーネントの作成

    `class-variance-authority` をインストールし、ボタンコンポーネントを作成します。`variant`(primary, secondary)と `size` プロパティを定義し、利用側からクラス名を隠蔽します。
  2. 2

    Step2: CSS 変数によるテーマ定義

    `app/globals.css` の `@theme` ブロック内に、ブランドカラーやフォント設定を CSS 変数として定義します。これにより `tailwind.config.js` なしでカスタマイズが可能になります。
  3. 3

    Step3: ダークモード設定

    `next-themes` を導入し、ルートレイアウトで `ThemeProvider` をラップします。CSS 変数を使って `.dark` クラス時の色定義を上書きすることで、シームレスな切り替えを実現します。
  4. 4

    Step4: 動的クラス名の排除

    コード内を検索し、文字列連結(`Use template literals`)でクラス名を生成している箇所を特定。オブジェクトマップ(`const map = { key: 'class-name' }`)方式に書き換えて、Purge 漏れを防ぎます。

FAQ

v4 はまだベータ版ですか?本番で使えますか?
記事執筆時点(2025年末)では安定版(Stable)がリリースされています。ただし、古いプラグインの一部が互換性を持っていない可能性があるため、移行前に依存ライブラリの対応状況を確認してください。
@apply は使ってもいいですか?
最小限に留めるべきです。サードパーティ製コンポーネントのスタイル上書きなど、どうしても必要な場合以外は、React コンポーネント化または `cva` の使用を推奨します。`@apply` を多用すると CSS ファイルが肥大化し、Tailwind のメリット(Atomic CSS)が失われます。
Shadcn UI は v4 に対応していますか?
はい、Shadcn UI の最新版は v4 に対応しており、内部で `cva` と `tailwind-merge` を使用しています。非常に相性が良いので、組み合わせて使うことを強くお勧めします。
CSS Modules との併用は可能ですか?
可能です。どうしても複雑なアニメーションや、Tailwind のユーティリティでは表現しきれないスタイルがある場合は、その部分だけ CSS Modules を使うハイブリッド構成も有効です。

4 min read · 公開日: 2025年12月20日 · 更新日: 2026年1月22日

コメント

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

関連記事