コンテンツにスキップ
App Routerガイドプリフェッチ

プリフェッチ

プリフェッチにより、アプリケーション内の異なるルート間のナビゲーションが瞬時に感じられます。Next.jsは、アプリケーションコードで使用されるリンクに基づいて、デフォルトでインテリジェントにプリフェッチしようとします。

このガイドでは、プリフェッチがどのように機能するかを説明し、一般的な実装パターンを示します。

プリフェッチはどのように機能しますか?

ルート間をナビゲートするとき、ブラウザはHTMLやJavaScriptファイルなどのアセットをリクエストします。プリフェッチは、新しいルートにナビゲートする前に、これらのリソースを事前に取得するプロセスです。

Next.jsは、ルートに基づいてアプリケーションを小さなJavaScriptチャンクに自動的に分割します。従来のSPAのようにすべてのコードを事前にロードするのではなく、現在のルートに必要なコードのみがロードされます。これにより、初期ロード時間が短縮され、アプリの他の部分はバックグラウンドでロードされます。リンクをクリックする頃には、新しいルートのリソースはすでにブラウザキャッシュにロードされています。

新しいページにナビゲートするとき、フルページリロードやブラウザのローディングスピナーはありません。代わりに、Next.jsはクライアントサイドトランジションを実行し、ページナビゲーションを瞬時に感じさせます。

静的ルートと動的ルートのプリフェッチ

静的ページ動的ページ
プリフェッチ済みはい、フルルートいいえ、loading.js を除く
クライアントキャッシュTTL5分(デフォルト)無効、有効化を除く
クリック時のサーバー往復いいえはい、シェル後にストリームされます

知っておくと良いこと: 初期ナビゲーション中に、ブラウザはHTML、JavaScript、およびReact Server Components (RSC)ペイロードを取得します。後続のナビゲーションでは、ブラウザはServer ComponentsのRSCペイロードとClient ComponentsのJSバンドルを取得します。

自動プリフェッチ

app/ui/nav-link.tsx
import Link from 'next/link'
 
export default function NavLink() {
  return <Link href="/about">About</Link>
}
コンテキストプリフェッチされたペイロードクライアントキャッシュTTL
loading.jsなしページ全体アプリの再読み込みまで
loading.jsありレイアウトから最初のローディング境界まで30秒(設定可能

自動プリフェッチは本番環境でのみ実行されます。prefetch={false}で無効にするか、プリフェッチの無効化のラッパーを使用してください。

手動プリフェッチ

手動プリフェッチを行うには、next/navigationからuseRouterフックをインポートし、router.prefetch()を呼び出して、ビューポート外または分析、ホバー、スクロールなどの応答でルートをウォームアップします。

'use client'
 
import { useRouter } from 'next/navigation'
import { CustomLink } from '@components/link'
 
export function PricingCard() {
  const router = useRouter()
 
  return (
    <div onMouseEnter={() => router.prefetch('/pricing')}>
      {/* other UI elements */}
      <CustomLink href="/pricing">View Pricing</CustomLink>
    </div>
  )
}

コンポーネントがロードされたときにURLをプリフェッチする意図がある場合は、リンクの拡張または除外の例を参照してください。

ホバーでトリガーされるプリフェッチ

注意して進めてください: Linkを拡張すると、プリフェッチ、キャッシュ無効化、アクセシビリティに関する懸念を維持することになります。デフォルトで不十分な場合にのみ進んでください。

Next.jsはデフォルトで適切なプリフェッチを実行しようとしますが、パワーユーザーはエジェクトしてニーズに基づいて変更できます。パフォーマンスとリソース消費の間で制御できます。

たとえば、ビューポートに入ったとき(デフォルトの動作)ではなく、ホバー時にのみプリフェッチをトリガーする必要がある場合があります。

'use client'
 
import Link from 'next/link'
import { useState } from 'react'
 
export function HoverPrefetchLink({
  href,
  children,
}: {
  href: string
  children: React.ReactNode
}) {
  const [active, setActive] = useState(false)
 
  return (
    <Link
      href={href}
      prefetch={active ? null : false}
      onMouseEnter={() => setActive(true)}
    >
      {children}
    </Link>
  )
}

prefetch={null}は、ユーザーが意図を示したときにデフォルト(静的)のプリフェッチを復元します。

<Link>コンポーネントを拡張して、独自のカスタムプリフェッチ戦略を作成できます。たとえば、ForesightJSライブラリを使用すると、ユーザーのカーソルの方向を予測してリンクをプリフェッチします。

または、useRouterを使用して、ネイティブ<Link>の動作の一部を再現することもできます。ただし、これによりプリフェッチとキャッシュ無効化の維持が義務付けられることに注意してください。

'use client'
 
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
 
function ManualPrefetchLink({
  href,
  children,
}: {
  href: string
  children: React.ReactNode
}) {
  const router = useRouter()
 
  useEffect(() => {
    let cancelled = false
    const poll = () => {
      if (!cancelled) router.prefetch(href, { onInvalidate: poll })
    }
    poll()
    return () => {
      cancelled = true
    }
  }, [href, router])
 
  return (
    <a
      href={href}
      onClick={(event) => {
        event.preventDefault()
        router.push(href)
      }}
    >
      {children}
    </a>
  )
}

onInvalidateは、Next.jsがキャッシュされたデータが古いと疑う場合に呼び出され、プリフェッチをリフレッシュできます。

知っておくと良いこと: aタグを使用すると、宛先ルートへのフルページナビゲーションが発生します。onClickを使用してフルページナビゲーションを防ぎ、次にrouter.pushを呼び出して宛先にナビゲートできます。

プリフェッチの無効化

リソース消費をより細かく制御するために、特定のルートのプリフェッチを完全に無効にできます。

'use client'
 
import Link, { LinkProps } from 'next/link'
 
function NoPrefetchLink({
  prefetch,
  ...rest
}: LinkProps & { children: React.ReactNode }) {
  return <Link {...rest} prefetch={false} />
}

たとえば、アプリケーションで一貫して<Link>を使用したい場合がありますが、フッターのリンクはビューポートに入ったときにプリフェッチする必要がない場合があります。

プリフェッチの最適化

知っておくと良いこと: レイアウトの重複排除とプリフェッチスケジューリングは、今後の最適化の一部です。現在、Next.js canaryでexperimental.clientSegmentCacheフラグを介して利用可能です。

クライアントキャッシュ

Next.jsは、ルートセグメントをキーとして、プリフェッチされたReact Server Componentペイロードをメモリに格納します。兄弟ルート間(例:/dashboard/settings/dashboard/analytics)をナビゲートするとき、親レイアウトを再利用し、更新されたリーフページのみを取得します。これにより、ネットワークトラフィックが削減され、ナビゲーション速度が向上します。

プリフェッチのスケジューリング

Next.jsは小さなタスクキューを維持し、以下の順序でプリフェッチします。

  1. ビューポート内のリンク
  2. ユーザーの意図を示すリンク(ホバーまたはタッチ)
  3. 新しいリンクが古いリンクを置き換えます
  4. 画面外にスクロールされたリンクは破棄されます

スケジューラは、未使用のダウンロードを最小限に抑えながら、可能性の高いナビゲーションを優先します。

部分プリレンダリング (PPR)

PPRが有効になっている場合、ページは静的シェルとストリーミングされた動的セクションに分割されます。

  • プリフェッチ可能なシェルはすぐにストリームされます。
  • 動的データは準備ができ次第ストリームされます。
  • データ無効化(revalidateTagrevalidatePath)は、関連するプリフェッチをサイレントにリフレッシュします。

トラブルシューティング

プリフェッチ中の意図しない副作用のトリガー

レイアウトやページが純粋でなく、副作用(例:分析の追跡)がある場合、これらはルートがプリフェッチされたときにトリガーされる可能性があり、ユーザーがページにアクセスしたときではありません。

これを回避するには、副作用をuseEffectフックまたはClient ComponentからトリガーされるServer Actionに移動する必要があります。

変更前:

app/dashboard/layout.tsx
import { trackPageView } from '@/lib/analytics'
 
export default function Layout({ children }: { children: React.ReactNode }) {
  // This runs during prefetch
  trackPageView()
 
  return <div>{children}</div>
}

変更後:

app/ui/analytics-tracker.tsx
'use client'
 
import { useEffect } from 'react'
import { trackPageView } from '@/lib/analytics'
 
export function AnalyticsTracker() {
  useEffect(() => {
    trackPageView()
  }, [])
 
  return null
}
app/dashboard/layout.tsx
import { AnalyticsTracker } from '@/app/ui/analytics-tracker'
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div>
      <AnalyticsTracker />
      {children}
    </div>
  )
}

プリフェッチが多すぎるのを防ぐ

Next.jsは、<Link>コンポーネントを使用すると、ビューポート内のリンクを自動的にプリフェッチします。

リソースの不要な使用を回避するためにこれを防ぎたい場合があります。たとえば、多数のリンクのリスト(無限スクロールテーブルなど)をレンダリングする場合などです。

<Link>コンポーネントのprefetchプロパティをfalseに設定することで、プリフェッチを無効にできます。

app/ui/no-prefetch-link.tsx
<Link prefetch={false} href={`/blog/${post.id}`}>
  {post.title}
</Link>

ただし、これは、静的ルートはクリック時にのみフェッチされ、動的ルートはサーバーがレンダリングを完了するまでナビゲーションを待つことを意味します。

プリフェッチを完全に無効にせずにリソース使用量を削減するには、ユーザーがリンクにホバーするまでプリフェッチを延期できます。これにより、ユーザーが訪問する可能性のあるリンクのみが対象となります。

app/ui/hover-prefetch-link.tsx
'use client'
 
import Link from 'next/link'
import { useState } from 'react'
 
export function HoverPrefetchLink({
  href,
  children,
}: {
  href: string
  children: React.ReactNode
}) {
  const [active, setActive] = useState(false)
 
  return (
    <Link
      href={href}
      prefetch={active ? null : false}
      onMouseEnter={() => setActive(true)}
    >
      {children}
    </Link>
  )
}