【Next.js入門 第2回】Next.jsのスタイリング手法を徹底解説!Tailwind CSS・CSS Modules・clsxの使い方【Learn Next.js】

Next.jsのCSSスタイリング4つの手法を解説するイメージ プログラミング

前回の第1回では、Next.jsの環境構築やWSL2でのトラブル対処法、そしてプロジェクトのディレクトリ構成について解説しました。

連載第2回となる今回は、Learn Next.js Chapter 2「CSS Styling」の内容をまとめていきます。

Next.jsではアプリにスタイルを適用する方法が複数用意されています。本記事では、公式チュートリアルで扱われているグローバルCSS、Tailwind CSS、CSS Modules、そして動的にクラス名を切り替えるclsxライブラリの使い方について分かりやすく解説します!

本記事で学ぶこと

  • グローバルCSS(global.css)の読み込み方(ルートレイアウトでの適用)
  • Tailwind CSSの基本と特徴
  • CSS Modulesを使ったコンポーネント単位のスタイリング
  • clsxを使った動的クラス名の切り替え
  • 公式クイズの解答と解説

1. グローバルCSS(Global Styles)の適用

アプリ全体に適用したいスタイル(CSSリセットや全体の背景色、フォント設定など)は、グローバルCSSファイルで管理します。

プロジェクトの /app/ui/ フォルダ内を確認すると、すでに global.css というファイルが用意されています。

ルートレイアウト(layout.tsx)で読み込む

Next.jsでは、アプリの最上位コンポーネントであるルートレイアウト(app/layout.tsx)でグローバルCSSをインポートするのがベストプラクティスです。

/app/layout.tsx を開き、最上部にインポート文を追加します。

// app/layout.tsx
import '@/app/ui/global.css';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

これで、アプリ全体に global.css のスタイルが適用されます。

実際に開発サーバーを起動して確認してみましょう。

npm run dev

前回で表示された画面と違い、以下のようにCSSが適用されていることが確認できます。

CSSが適用された後のWEBページのスクリーンショット

2. Tailwind CSSの基本

global.css の中身を見てみると、以下のような記述があります。

/* app/ui/global.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

Next.jsでは create-next-app でプロジェクトを作成する際、デフォルトで Tailwind CSS がセットアップされます。

Tailwind CSSとは?

JSXの className に text-blue-500 や flex といったユーティリティクラスを直接書いてスタイリングするCSSフレームワークです。

CSSファイルを別で作る必要がなくなり、クラス名の命名に悩まないこともなく、使用していないCSSが自動で削除(Tree-shaking)されるため、ファイルサイズも小さくなります。

従来のCSSとTailwind CSSのスタイリング手法を比較した図解

例えば、/app/page.tsxのコードの中の以下の部分は、ヘッダーエリア(ただの青い箱)の表しています

<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
  {/* <AcmeLogo /> */}
</div>
  • h-20 md:h-52: モバイル(デフォルト)では高さ 5rem(80px)、中型画面以上(md:)では高さ 13rem(208px)に変化するレスポンシブデザインです。
  • bg-blue-500: 青色の背景を設定しています。
  • items-end: 内部のロゴを底面(下揃え)に配置します。
  • shrink-0: 親要素(flexコンテナ)の高さが小さくなっても、このヘッダー自体が縮まないように固定します。

現在は内側の {/* <AcmeLogo/> */} という行がコメントアウト(無効化)されているため、中にロゴが表示されず「ただの青い箱」に見えています。

    Tailwindで三角形を描画してみる

    実験として、/app/page.tsx に以下のコードを記述してみます。

    // app/page.tsx
    <div
      className="relative w-0 h-0 border-l-[15px] border-r-[15px] border-b-[26px] border-l-transparent border-r-transparent border-b-black"
    />

    このdivタグを入れたところに黒い三角形が表示されたのが開発サーバーで確認できると思います。

    ❓ 公式クイズ 1 上記のTailwindクラスを適用したとき、画面にはどんな図形が表示されるでしょうか?
    正解:C(黒い三角形 / A black triangle) 左右のボーダーを透明(transparent)にし、下ボーダーに黒色を設定することで三角形を生成しています。

    3. CSS Modulesを使ったコンポーネント限定のスタイリング

    「従来通りのCSSファイルでスタイルを管理したい」「JSX内に大量のクラス名を書きたくない」という場合に最適なのが CSS Modules です。

    CSS Modulesを使用すると、ビルド時にユニークなクラス名が自動生成されるため、別ファイルのCSSとクラス名が衝突する(スタイルがバッティングする)心配がありません。

    CSS Modulesの使い方

    1. /app/ui/home.module.css を新規作成し、以下のスタイルを記述します。
    /* app/ui/home.module.css */
    .shape {
      height: 0;
      width: 0;
      border-bottom: 30px solid black;
      border-left: 20px solid transparent;
      border-right: 20px solid transparent;
    }
    1. /app/page.tsx でインポートして適用します。
    // app/page.tsx
    import styles from '@/app/ui/home.module.css';
    
    export default function Page() {
      return (
        <main className="flex min-h-screen flex-col p-6">
          <div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
            {/* ... */}
    
            <div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
              
              {/* ★ここに CSS Modules のクラスを適用した div を配置します */}
              <div className={styles.shape} />
          {/* ... */}
        </main>
      );
    }

    すると以下のようにCSS Modulesで定義された黒い三角形が開発サーバーの画面に追加されます。

    CSSモジュールを適用させた後の開発サーバーのスクリーンショット

    ❓ 公式クイズ 2 CSS Modulesを使用する利点は何でしょうか?
    正解:B(CSSクラスをコンポーネントローカルなスコープにし、スタイルの衝突リスクを減らす)

    4. clsx ライブラリによる動的なクラス名の切り替え

    条件に応じてスタイルのクラス名を動的に変更したい場合(例:ステータスが「支払い済み」なら緑、「保留」ならグレー)、clsx というユーティリティライブラリを使用するとコードがすっきり書けます。

    clsx の使用例

    請求書ステータスコンポーネント(/app/ui/invoices/status.tsx)の例を見てみましょう。

    // app/ui/invoices/status.tsx
    import clsx from 'clsx';
    
    export default function InvoiceStatus({ status }: { status: string }) {
      return (
        <span
          className={clsx(
            'inline-flex items-center rounded-full px-2 py-1 text-sm', // 共通スタイル
            {
              'bg-gray-100 text-gray-500': status === 'pending', // pendingの時だけ適用
              'bg-green-500 text-white': status === 'paid',       // paidの時だけ適用
            },
          )}
        >
          {status}
        </span>
      );
    }

    clsx の第2引数にオブジェクトを渡すことで、条件(status === ‘paid’)が true の時だけ指定したクラス名が付与されるようになります。三項演算子を複雑にネストさせる必要がなくなり、非常に可読性が上がります。

    app/page.tsxにこのstatus.tsxをインポートして開発サーバーに表示させてみましょう。

    import InvoiceStatus from '@/app/ui/invoices/status'; // ★1. インポートを追加
    
    export default function Page() {
      return (
        <main className="flex min-h-screen flex-col p-6">
          {/* ... */}
          <div className="mt-4 flex grow flex-col gap-4 md:flex-row">
            <div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
              
              {/* ★2. 動作確認用に pending と paid の両方を表示してみる */}
              <div className="flex gap-2">
                <InvoiceStatus status="pending" />
                <InvoiceStatus status="paid" />
              </div>

    以下のような画面が表示されればテストOKです。

    clsxを適用した後の開発サーバー画面のスクリーンショット

    ❓ 公式クイズ 3 エディタで clsx を検索した際、どのコンポーネントで使われているでしょうか?
    正解:A(status.tsx と pagination.tsx)

    5. その他のスタイリング手法

    Next.jsでは上記以外にもさまざまなスタイリング手法がサポートされています。

    • Sass: .scss や .sass ファイルのインポートに対応
    • CSS-in-JS: styled-jsx、styled-components、emotion など

    プロジェクトの規模やチームの好みに合わせて柔軟に選択可能です。

    まとめと次回予告

    今回はNext.jsにおけるCSSスタイリングの基礎について解説しました。

    • 全体適用 ➔ app/layout.tsx で global.css をインポート
    • メインの手法 ➔ 開発スピードが上がる Tailwind CSS
    • カプセル化 ➔ クラス名の衝突を防ぐ CSS Modules
    • 条件分岐 ➔ 可読性を高める clsx

    次回(第3回)は、「フォントと画像の最適化(next/font と next/image)」について学んでいきます。Next.jsがWebサイトのパフォーマンスをどのように自動で向上させてくれるのかを体験していきましょう!

    コメント