Web制作

Next.js App Routerでパフォーマンスを本気で上げる実践ガイド

App Routerに移行したけどパフォーマンスが上がらない、という声をよく聞きます。Server Components、Streaming SSR、画像最適化、フォント最適化まで、実務で効果が出た手法をまとめました。

Tufe Company·Engineering2026年1月28日12分で読める

「App Routerに変えたのに遅い」問題

Next.js App Routerに移行したら、自動的にサイトが速くなると思っていませんか。

残念ながら、そうはなりません。App Router自体は強力なアーキテクチャですが、正しく使わないとPages Routerより遅くなるケースすらあります。

私たちTufe Companyが手がけたプロジェクトでも、App Router移行直後にLighthouseスコアが30点下がったケースがありました。原因を調査して最適化した結果、最終的には移行前より20点以上改善。Performanceスコア94、LCPは1.2秒まで持っていけました。

この記事では、その過程で得た知見を全部共有します。

0+
目標Lighthouse スコア
0%
JSバンドル サイズ削減
0.5秒以内
LCP 目標値

Server Componentsの力を正しく引き出す

App Routerの最大の武器はServer Componentsです。でも、この使い分けを間違えている人が本当に多い。

Server Componentsがデフォルトという意味

App Routerでは、すべてのコンポーネントがデフォルトでServer Componentになります。ブラウザではなくサーバー側でレンダリングされ、HTMLだけがクライアントに送られる。

つまり、JavaScriptバンドルにコンポーネントのコードが含まれない。これがパフォーマンス改善の核心です。

"use client" を安易に書かない

ここが最大の落とし穴。useStateuseEffectを使いたいからと、コンポーネントの先頭に"use client"を書く。すると、そのコンポーネントとその子コンポーネントすべてがClient Componentになる。

tsx
// ❌ こうしがち — ページ全体がClient Componentに
"use client"

export default function ProductPage() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <ProductDetails />  {/* これもClient Componentになる */}
      <Reviews />          {/* これもClient Componentになる */}
      <AddToCartButton count={count} setCount={setCount} />
    </div>
  )
}
tsx
// ✅ こうする — インタラクティブな部分だけClient Component
export default function ProductPage() {
  return (
    <div>
      <ProductDetails />  {/* Server Component のまま */}
      <Reviews />          {/* Server Component のまま */}
      <AddToCartButton />  {/* これだけ "use client" */}
    </div>
  )
}

私たちの経験則では、Client Componentはページ全体の20%以下に抑えるのが理想。ボタンのクリック、フォームの入力、アニメーションなど、本当にブラウザ側の処理が必要な部分だけに限定します。

Component Types

Server Components(デフォルト)

サーバーでレンダリング、JSゼロ

JSバンドルに含まれない → 高速
DBやファイルシステムに直接アクセス
APIキーなどの機密情報を安全に使用
データ取得がシンプル(async/await)

Streaming SSRで体感速度を劇的に改善する

Server Componentsと並んで強力なのがStreaming SSRです。

従来のSSRとの違い

従来のSSR(Pages Router)では、ページのすべてのデータが揃ってからHTMLをまとめて送信していました。APIの応答が遅いコンポーネントが1つあると、ページ全体の表示が遅れる。

Streaming SSRでは、準備ができた部分から順次HTMLを送信します。ヘッダーとナビゲーションは即座に表示されて、データの取得が必要な部分はloading.tsxのフォールバックUIを表示しながら、裏でデータを取得して完了次第差し替える。

Suspenseを戦略的に配置する

loading.tsxはルートセグメント単位のフォールバックですが、より細かい制御には<Suspense>を直接使います。

tsx
import { Suspense } from 'react'

export default function DashboardPage() {
  return (
    <div>
      <h1>ダッシュボード</h1>
      <Suspense fallback={<StatsSkeleton />}>
        <StatsPanel />  {/* DBクエリに2秒かかる */}
      </Suspense>
      <Suspense fallback={<ChartSkeleton />}>
        <RevenueChart />  {/* 外部API呼び出しに3秒かかる */}
      </Suspense>
      <RecentActivity />  {/* キャッシュ済みで即座に表示 */}
    </div>
  )
}

ポイントは、遅いコンポーネントを個別にSuspenseで囲むこと。そうすることで、速いコンポーネントの表示が遅いコンポーネントに引きずられなくなります。

画像最適化 — next/imageを正しく使う

Webパフォーマンスの問題の約60%は画像に起因します。next/imageコンポーネントは強力ですが、設定を間違えると効果が半減します。

sizes属性を必ず指定する

sizesを省略すると、Next.jsはビューポート幅100%を前提にした画像を生成します。サイドバーの中にある300px幅の画像に、1920px幅の画像が読み込まれる。無駄です。

tsx
// ❌ sizesを省略
<Image src="/hero.jpg" width={800} height={600} alt="hero" />

// ✅ sizesを正しく指定
<Image
  src="/hero.jpg"
  width={800}
  height={600}
  alt="hero"
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px"
/>

priority属性はファーストビューの画像だけ

priorityを指定すると画像がプリロードされますが、ファーストビュー(スクロールせずに見える範囲)の画像だけに使ってください。それ以外の画像に付けると、逆にLCPが悪化します。

外部画像のドメイン設定

外部のCDNやCMSから画像を読み込む場合、next.config.jsimages.remotePatternsに許可するドメインを設定してください。設定しないとnext/imageの最適化が効きません。

フォント最適化 — next/fontの活用

Webフォントの読み込みは、CLSとLCPの両方に影響します。next/fontを使えば、これを自動的に最適化できます。

tsx
import { Noto_Sans_JP } from 'next/font/google'

const notoSansJP = Noto_Sans_JP({
  subsets: ['latin'],
  weight: ['400', '700'],
  display: 'swap',
  preload: true,
})

export default function RootLayout({ children }) {
  return (
    <html lang="ja" className={notoSansJP.className}>
      <body>{children}</body>
    </html>
  )
}

next/fontはビルド時にフォントファイルをダウンロードしてセルフホスティングします。Google Fontsへの外部リクエストが不要になるので、接続のオーバーヘッドがゼロに。

日本語フォントはファイルサイズが大きいので、weightを必要な太さだけに限定するのが大事です。全ウェイト(100〜900)を読み込むのは絶対に避けてください。

Optimization Techniques

画像最適化

AVIF/WebP自動変換、レスポンシブサイズ、遅延読み込み

フォント最適化

next/fontでゼロレイアウトシフト。必要なウェイトのみ読み込み

Streaming SSR

Suspenseで部分的にレンダリング。ユーザーは待たない

Core Web Vitals

LCP, FID, CLSを継続的にモニタリングし改善

Core Web Vitalsの改善チェックリスト

最後に、私たちが実際のプロジェクトで使っているチェックリストを共有します。

LCP(Largest Contentful Paint)を2.5秒以内にする

  • ファーストビューの画像にpriorityを設定しているか
  • Server Componentsで不要なJSバンドルを削減しているか
  • 外部フォントをnext/fontでセルフホスティングしているか
  • sizes属性で適切なレスポンシブ画像を配信しているか

INP(Interaction to Next Paint)を200ms以内にする

  • 重いClient Componentを動的インポート(next/dynamic)で遅延読み込みしているか
  • イベントハンドラ内で重い処理をメインスレッドで実行していないか
  • useTransitionを使って非緊急な状態更新を後回しにしているか

CLS(Cumulative Layout Shift)を0.1以内にする

  • 画像にwidthheightを明示しているか
  • Webフォントの読み込みでdisplay: 'swap'を使っているか
  • 動的に挿入されるコンテンツ(広告、バナー等)にスペースを確保しているか
App Router パフォーマンスチェックリスト
0 / 5

パフォーマンスは「後から直す」では手遅れになる

Webサイトのパフォーマンスは、設計段階で決まります。完成してから「遅いから直して」では、アーキテクチャレベルの変更が必要になることが多く、コストが膨大になる。

App Routerは正しく使えば圧倒的に速いサイトを作れます。逆に、なんとなく使うと従来より遅くなることもある。

大事なのは、パフォーマンスバジェットを最初に決めて、開発中に継続的に計測すること。LCP 2.5秒以内、INP 200ms以内、CLS 0.1以内。この数字をチーム全員が意識しながら開発する。

Webサイトのパフォーマンス改善にお困りの方へ。 Tufe Companyでは、Next.js / App Routerを活用したパフォーマンス最適化の支援を行っています。Lighthouseスコアの改善からCore Web Vitalsの継続的なモニタリングまで、お気軽にご相談ください。

Next.jsApp Routerパフォーマンス最適化Server ComponentsCore Web VitalsReact
Next Step

AI × 自動化で、ビジネスを加速させませんか?

Tufe Companyは、AI・SEO・Web制作を通じて中小企業のデジタル集客と業務効率化を支援します。

Related Articles