Next.js の細かい落とし穴まとめ|App Router 時代にハマりやすい4つのポイント

Next.js(特に App Router)は便利な反面、**「あれ?なんでエラー?」**という落とし穴も多いフレームワークです。
この記事では初心者〜中級者がよくつまずくポイントを 4つに厳選して、分かりやすく解説します。

  • App Router で “use client” が必要か判断する基準
  • 動的ルートで hydration error が出る時の対処法
  • 画像アップロード時にバンドルが肥大化する原因
  • Edge Runtime と Node.js Runtime の違い

Next.js を使った開発で悩んだら、ここをチェックすればほぼ解決できます。


1. App Router で “use client” が必要かどうか判断する基準

App Router では、デフォルトがサーバーコンポーネントです。
そのため、ほんの少しでもクライアント側の処理が混ざるとエラーになります。

🔍 “use client” が必要になる条件まとめ

条件説明
ブラウザAPIを使うwindow localStorage document など
React Hooks を使うuseState useEffect useRef など
ユーザー操作が必要ボタンで動く処理、フォーム入力など
アニメーション・モーダルFramer Motion などブラウザ依存のUI

これらが1つでも当てはまる場合は、ファイルの先頭に

"use client";

を付けましょう。


❌ “use client” を付けなくて良いケース

付けなくてOK理由
データ取得だけのコンポーネントサーバー側で完結するため
API から取得した情報を表示するだけSSRで十分対応できる
Layout や metadata の設定すべてサーバーコンポーネントでOK

💡 基本ルール(覚え方)

「ユーザーが触るなら client」「読ませるだけなら server」

これを覚えておくと判断が非常にラクになります。


2. 動的ルートで Hydration Error が出るときの対処法

Next.js のエラーで最も多いのが hydration error(ハイドレーションエラー)

特に /user/[uid]/post/[id] など 動的ルートで発生しやすいのが特徴です。

🔥 Hydration Error が起きる代表的な原因

  1. サーバー側とクライアント側で表示がズレる
    • 日付のフォーマット
    • ランダム値(Math.random()
    • 初期描画で undefined → 後から値が入る UI
  2. 動的パラメータをクライアント側で誤って取得している
    • useEffect の結果とサーバー側の値が不一致
  3. 非同期読み込みの結果がタイミングで変わる

✔ 対処法(最重要)

(1) サーバー側で値を確定させる

App Router では 非同期 Server Component が使えるため、

export default async function Page({ params }) {
  const data = await fetch(...);
  return <UI data={data} />;
}

のように 初期描画段階でデータを確定させるのが最も安全です。


(2) クライアント側で値が変わる処理はプレースホルダーを使う

{profile ? <ProfileCard /> : <LoadingSkeleton />}

こうすると React が描画差分を正しく扱えるため、ズレが出ません。


(3) ランダム値・日時はサーバー側で固定

new Date().toLocaleString()

これをクライアントとサーバーで実行すると 100%ズレます。


3. 画像アップロード時にバンドルが肥大化する原因

Next.js + 画像アップロードの実装でよくあるのが、

  • ビルドが重い
  • バンドルサイズが急に肥大化
  • デプロイが遅い

という問題です。

原因は主に以下の通り。


🧨 原因1:画像処理ライブラリをクライアントに含めている

  • sharp
  • jimp
  • browser-image-compression
  • react-image-crop など

本来サーバーで実行すべきロジックが、クライアント側に流れると
バンドルサイズが数MB〜数十MBに膨れ上がることも。


✔ 対処法

(1) 「画像処理」はサーバーに寄せる

Next.js App Routerでは Route Handler が使えるため、

  • /api/upload
  • /api/resize-image

などをサーバー側に作り、そちらに処理を集約します。


(2) 画像読み込みは <Image /> を使う

Next.js の Image コンポーネントは 最適化済みで、
自動的に軽量化・遅延読み込みされるためバンドル肥大化を防げます。


(3) データURL (base64) を肥大させない

base64は ファイルサイズが約33%増えるため、
そのまま保存すると重くなりやすいです。

→ 必ず Storage や S3 にアップロードし、URL だけ Firestore に保存しましょう。


4. Edge Runtime と Node.js Runtime の違い(初心者向けの最速理解)

✨ 結論から

Edge Runtime:超高速・軽量だけど制約多い
Node.js Runtime:自由度が高く、ほぼなんでも動く

この理解でOKです。


🧩 Edge Runtime(Cloudflare Workers のような動作)

  • 非常に高速
  • グローバルにキャッシュされる
  • 起動コストが低い
  • Cold Start がほぼゼロ
  • ただし制約が多い(fscrypto.randomUUID()など使えない)

Next.js の例:

export const runtime = "edge";

用途:

  • 軽いAPI
  • 認証チェック
  • レスポンス高速化が重要な処理

🧩 Node.js Runtime(従来のサーバー)

  • とにかく自由度が高い
  • Firebase SDK、stripe、image処理などほぼ全部動く
  • バックエンド向き
  • 速度はEdgeほどではないが十分速い

Next.js の例:

export const runtime = "nodejs";

用途:

  • 画像アップロード
  • Firestore / Storage の書き込み
  • 重いサーバー処理
  • 外部API統合

まとめ|Next.js は理解すれば怖くない

今回紹介した落とし穴は、Next.js App Router で特に起きやすいトラブルです。

  • “use client” はユーザーが触る時だけ
  • Hydration Error はサーバーとクライアントの差分が原因
  • 画像アップロードはサーバー側に寄せるのが正解
  • Edge Runtime と Node.js Runtime は速度と自由度で住み分ける

この4つを押さえておくと、Next.js の開発が一気にスムーズになります。

おすすめの記事