Sponsor
ChatHubChatHub Use GPT-4, Gemini, Claude 3.5 and more chatbots side-by-side
ここをクリック
Menu

リンクとナビゲーション

Next.jsではルート間を移動するための4つの方法があります:

このページではこれらの各オプションの使用方法と、ナビゲーションの仕組みについて詳しく説明します。

<Link>はHTMLの<a>タグを拡張した組み込みコンポーネントで、ルート間のプリフェッチとクライアントサイドナビゲーションを提供します。これはNext.jsでルート間を移動するための主要で推奨される方法です。

next/linkからインポートし、コンポーネントにhrefプロップを渡すことで使用できます:

app/page.tsx
TypeScript
import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}

<Link>に渡せるオプションのプロップが他にもあります。詳細はAPIリファレンスをご覧ください。

useRouter() フック

useRouterフックを使用すると、クライアントコンポーネントからプログラムでルートを変更できます。

app/page.tsx
TypeScript
'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

useRouterのメソッド一覧については、APIリファレンスをご覧ください。

推奨: useRouterを使用する特定の要件がない限り、ルート間の移動には<Link>コンポーネントを使用してください。

redirect 関数

サーバーコンポーネントでは、代わりにredirect関数を使用します。

app/team/[id]/page.tsx
TypeScript
import { redirect } from 'next/navigation'
 
async function fetchTeam(id: string) {
  const res = await fetch('https://...')
  if (!res.ok) return undefined
  return res.json()
}
 
export default async function Profile({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const id = (await params).id
  if (!id) {
    redirect('/login')
  }
 
  const team = await fetchTeam(id)
  if (!team) {
    redirect('/join')
  }
 
  // ...
}

補足:

  • redirectはデフォルトで307(一時的なリダイレクト)ステータスコードを返します。Server Actionで使用する場合は303(See Other)を返し、これはPOSTリクエストの結果として成功ページにリダイレクトする際によく使用されます。
  • redirectは内部的にエラーをスローするため、try/catchブロックの外で呼び出す必要があります。
  • redirectはレンダリングプロセス中にクライアントコンポーネントで呼び出すことができますが、イベントハンドラ内では使用できません。代わりにuseRouterフックを使用できます。
  • redirectは絶対URLも受け付け、外部リンクへのリダイレクトにも使用できます。
  • レンダリングプロセスの前にリダイレクトしたい場合は、next.config.jsまたはミドルウェアを使用してください。

詳細についてはredirect APIリファレンスをご覧ください。

ネイティブHistory APIの使用

Next.jsでは、ページをリロードせずにブラウザの履歴スタックを更新するために、ネイティブのwindow.history.pushStateおよびwindow.history.replaceStateメソッドを使用できます。

pushStatereplaceStateの呼び出しはNext.jsルーターと統合されており、usePathnameuseSearchParamsと同期できます。

window.history.pushState

ブラウザの履歴スタックに新しいエントリを追加するために使用します。ユーザーは前の状態に戻ることができます。例えば、製品リストをソートする場合:

'use client'
 
import { useSearchParams } from 'next/navigation'
 
export default function SortProducts() {
  const searchParams = useSearchParams()
 
  function updateSorting(sortOrder: string) {
    const params = new URLSearchParams(searchParams.toString())
    params.set('sort', sortOrder)
    window.history.pushState(null, '', `?${params.toString()}`)
  }
 
  return (
    <>
      <button onClick={() => updateSorting('asc')}>Sort Ascending</button>
      <button onClick={() => updateSorting('desc')}>Sort Descending</button>
    </>
  )
}
'use client'
 
import { useSearchParams } from 'next/navigation'
 
export default function SortProducts() {
  const searchParams = useSearchParams()
 
  function updateSorting(sortOrder) {
    const params = new URLSearchParams(searchParams.toString())
    params.set('sort', sortOrder)
    window.history.pushState(null, '', `?${params.toString()}`)
  }
 
  return (
    <>
      <button onClick={() => updateSorting('asc')}>Sort Ascending</button>
      <button onClick={() => updateSorting('desc')}>Sort Descending</button>
    </>
  )
}

window.history.replaceState

ブラウザの履歴スタック上の現在のエントリを置き換えるために使用します。ユーザーは前の状態に戻ることができません。例えば、アプリケーションのロケールを切り替える場合:

'use client'
 
import { usePathname } from 'next/navigation'
 
export function LocaleSwitcher() {
  const pathname = usePathname()
 
  function switchLocale(locale: string) {
    // 例:'/en/about'または'/fr/contact'
    const newPath = `/${locale}${pathname}`
    window.history.replaceState(null, '', newPath)
  }
 
  return (
    <>
      <button onClick={() => switchLocale('en')}>English</button>
      <button onClick={() => switchLocale('fr')}>French</button>
    </>
  )
}
'use client'
 
import { usePathname } from 'next/navigation'
 
export function LocaleSwitcher() {
  const pathname = usePathname()
 
  function switchLocale(locale) {
    // 例:'/en/about'または'/fr/contact'
    const newPath = `/${locale}${pathname}`
    window.history.replaceState(null, '', newPath)
  }
 
  return (
    <>
      <button onClick={() => switchLocale('en')}>English</button>
      <button onClick={() => switchLocale('fr')}>French</button>
    </>
  )
}

ルーティングとナビゲーションの仕組み

Appルーターはルーティングとナビゲーションにハイブリッドアプローチを使用します。サーバー上では、アプリケーションコードがルートセグメントによって自動的にコード分割されます。そしてクライアント上では、Next.jsがルートセグメントをプリフェッチしてキャッシュします。これにより、ユーザーが新しいルートに移動した際にブラウザがページをリロードせず、変更されたルートセグメントのみが再レンダリングされるため、ナビゲーション体験とパフォーマンスが向上します。

1. コード分割

コード分割により、アプリケーションコードを小さなバンドルに分割してブラウザによってダウンロードおよび実行できるようになります。これにより、各リクエストで転送されるデータ量と実行時間が削減され、パフォーマンスが向上します。

サーバーコンポーネントにより、アプリケーションコードはルートセグメントによって自動的にコード分割されます。これにより、ナビゲーション時に現在のルートに必要なコードのみが読み込まれます。

2. プリフェッチ

プリフェッチはユーザーがルートを訪問する前にバックグラウンドでルートを事前に読み込む方法です。

Next.jsでは、ルートが2つの方法でプリフェッチされます:

  • <Link>コンポーネント:ルートはユーザーのビューポートに表示されると自動的にプリフェッチされます。プリフェッチはページが最初に読み込まれた時、またはスクロールによって表示された時に行われます。
  • router.prefetch()useRouterフックを使用してプログラムでルートをプリフェッチできます。

<Link>のデフォルトのプリフェッチ動作(prefetchプロップが未指定またはnullに設定されている場合)は、loading.jsの使用方法によって異なります。共有レイアウトは、コンポーネントの「ツリー」から最初のloading.jsファイルまでレンダリングされてプリフェッチされ、30s間キャッシュされます。これにより動的ルート全体をフェッチするコストが削減され、ユーザーへの視覚的なフィードバックを向上させるためのインスタントローディング状態を表示できます。

prefetchプロップをfalseに設定することでプリフェッチを無効にできます。あるいは、prefetchプロップをtrueに設定することで、ローディング境界を超えて完全なページデータをプリフェッチすることもできます。

詳細については<Link> APIリファレンスをご覧ください。

補足:

  • プリフェッチは開発環境では有効ではなく、本番環境でのみ有効です。

3. キャッシング

Next.jsにはルーターキャッシュと呼ばれるインメモリのクライアントサイドキャッシュがあります。ユーザーがアプリケーション内を移動すると、プリフェッチされたルートセグメントと訪問済みルートのReact Server Componentペイロードがキャッシュに保存されます。

これにより、ナビゲーション時に新たにサーバーへリクエストを送信する代わりに、可能な限りキャッシュが再利用されます。リクエスト数とデータ転送量を削減することでパフォーマンスが向上します。

ルーターキャッシュの仕組みとその設定方法について詳しく学んでください。

4. 部分レンダリング

部分レンダリングとは、ナビゲーション時に変更されたルートセグメントのみがクライアント上で再レンダリングされ、共有セグメントは保持されることを意味します。

例えば、2つの兄弟ルート/dashboard/settings/dashboard/analytics間を移動する場合、settingsページがアンマウントされ、analyticsページが新しい状態でマウントされ、共有のdashboardレイアウトは保持されます。この動作は同じ動的セグメント上の2つのルート間でも存在します。例えば/blog/[slug]/pageで、/blog/firstから/blog/secondへ移動する場合です。

部分レンダリングの仕組み

部分レンダリングがなければ、各ナビゲーションでクライアント上の完全なページが再レンダリングされます。変更されたセグメントのみをレンダリングすることで、転送されるデータ量と実行時間が削減され、パフォーマンスが向上します。

5. ソフトナビゲーション

ブラウザはページ間の移動時に「ハードナビゲーション」を実行します。Next.js Appルーターはページ間の「ソフトナビゲーション」を可能にし、変更されたルートセグメントのみが再レンダリングされるようにします(部分レンダリング)。これによりナビゲーション中にクライアントReact状態が保持されます。

6. 戻るおよび進むナビゲーション

デフォルトでは、Next.jsは戻るおよび進むナビゲーションでスクロール位置を維持し、ルーターキャッシュ内のルートセグメントを再利用します。

7. pages/app/間のルーティング

pages/からapp/へ段階的に移行する際、Next.jsルーターは自動的に両者間のハードナビゲーションを処理します。pages/からapp/への遷移を検出するために、クライアントルーターフィルターはアプリルートの確率的チェックを利用しており、これが時に偽陽性を引き起こす可能性があります。デフォルトでは、偽陽性の可能性を0.01%に設定しているため、このような発生は非常にまれであるはずです。この確率はnext.config.jsexperimental.clientRouterFilterAllowedRateオプションでカスタマイズできます。偽陽性率を下げるとクライアントバンドルで生成されるフィルターのサイズが大きくなることに注意が必要です。

あるいは、この処理を完全に無効にしてpages/app/間のルーティングを手動で管理したい場合は、next.config.jsexperimental.clientRouterFilterfalseに設定できます。この機能が無効になっている場合、アプリルートと重複するpages内の動的ルートはデフォルトでは適切にナビゲートされません。