リンクとナビゲーション
Next.jsでルート間をナビゲートする方法は4つあります:
<Link>
コンポーネントを使用するuseRouter
フックを使用する(Client Components)redirect
関数を使用する(Server Components)- ネイティブのHistory APIを使用する
このページでは、これらのオプションの使い方を説明し、ナビゲーションの仕組みについて詳しく解説します。
<Link>
コンポーネント
<Link>
は、HTMLの<a>
タグを拡張して、プリフェッチとクライアントサイドのルート間ナビゲーションを提供する組み込みコンポーネントです。Next.jsでルート間をナビゲートするための主要で推奨される方法です。
next/link
からインポートし、コンポーネントにhref
プロップを渡すことで使用できます:
- TypeScript
- JavaScript
import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>
}
import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>
}
<Link>
に渡すことができる他のオプションのプロップもあります。詳細はAPIリファレンスを参照してください。
useRouter()
フック
useRouter
フックを使用すると、Client Componentsからプログラム的にルートを変更できます。
- TypeScript
- JavaScript
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}
'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リファレンスを参照してください。
推奨事項: 特定の要件がない限り、ルート間のナビゲーションには
<Link>
コンポーネントを使用してください。
redirect
関数
Server Componentsの場合は、代わりにredirect
関数を使用します。
- TypeScript
- JavaScript
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')
}
// ...
}
import { redirect } from 'next/navigation'
async function fetchTeam(id) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({ params }) {
const id = (await params).id
if (!id) {
redirect('/login')
}
const team = await fetchTeam(id)
if (!team) {
redirect('/join')
}
// ...
}
Good to know:
redirect
はデフォルトで307(Temporary Redirect)ステータスコードを返します。Server Actionで使用される場合、POSTリクエストの結果として成功ページにリダイレクトするために一般的に使用される303(See Other)を返します。redirect
は内部的にエラーをスローするため、try/catch
ブロックの外で呼び出す必要があります。redirect
は、レンダリングプロセス中にClient Componentsで呼び出すことができますが、イベントハンドラ内では呼び出せません。代わりにuseRouter
フックを使用できます。redirect
は絶対URLも受け入れ、外部リンクへのリダイレクトに使用できます。- レンダリングプロセスの前にリダイレクトしたい場合は、
next.config.js
またはMiddlewareを使用してください。
詳細については、redirect
APIリファレンスを参照してください。
ネイティブのHistory APIを使用する
Next.jsでは、ネイティブのwindow.history.pushState
とwindow.history.replaceState
メソッドを使用して、ページをリロードせずにブラウザの履歴スタックを更新できます。
pushState
とreplaceState
の呼び出しはNext.js Routerに統合され、usePathname
やuseSearchParams
と同期できます。
window.history.pushState
ブラウザの履歴スタックに新しいエントリを追加するために使用します。ユーザーは前の状態に戻ることができます。たとえば、製品のリストをソートするには:
- TypeScript
- JavaScript
'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
ブラウザの履歴スタックの現在のエントリを置き換えるために使用します。ユーザーは前の状態に戻ることができません。たとえば、アプリケーションのロケールを切り替えるには:
- TypeScript
- JavaScript
'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 Routerは、ルーティングとナビゲーションにハイブリッドアプローチを使用します。サーバー上では、アプリケーションコードがルートセグメントによって自動的にコード分割されます。そしてクライアント上では、Next.jsがルートセグメントをプリフェッチし、キャッシュします。これにより、ユーザーが新しいルートに移動するとき、ブラウザはページをリロードせず、変更されたルートセグメントのみが再レンダリングされ、ナビゲーション体験とパフォーマンスが向上します。
1. コード分割
コード分割により、アプリケーションコードを小さなバンドルに分割し、ブラウザによってダウンロードおよび実行されるようにします。これにより、転送されるデータ量と各リクエストの実行時間が削減され、パフォーマンスが向上します。
Server Componentsは、アプリケーションコードをルートセグメントによって自動的にコード分割することを可能にします。これにより、ナビゲーション時に現在のルートに必要なコードのみが読み込まれます。
2. プリフェッチ
プリフェッチは、ユーザーが訪れる前にバックグラウンドでルートを事前に読み込む方法です。
Next.jsでは、ルートがプリフェッチされる方法は2つあります:
<Link>
コンポーネント:ルートはユーザーのビューポートに表示されると自動的にプリフェッチされます。プリフェッチは、ページが最初に読み込まれたときや、スクロールによって表示されるときに行われます。router.prefetch()
:useRouter
フックを使用して、プログラム的にルートをプリフェッチできます。
<Link>
のデフォルトのプリフェッチ動作(つまり、prefetch
プロップが指定されていないか、null
に設定されている場合)は、loading.js
の使用状況に応じて異なります。共有レイアウトのみがプリフェッチされ、loading.js
ファイルまでのレンダリングされた"tree"のコンポーネントがキャッシュされ、30秒
間保持されます。これにより、動的ルート全体をフェッチするコストが削減され、ユーザーに対してより良い視覚的フィードバックを提供するために即時のローディング状態を表示できます。
プリフェッチを無効にするには、prefetch
プロップをfalse
に設定します。あるいは、prefetch
プロップをtrue
に設定することで、ローディング境界を超えて完全なページデータをプリフェッチできます。
詳細については、<Link>
APIリファレンスを参照してください。
Good to know:
- プリフェッチは開発環境では有効ではなく、本番環境でのみ有効です。
3. キャッシング
Next.jsには、Router Cacheと呼ばれるインメモリのクライアントサイドキャッシュがあります。ユーザーがアプリをナビゲートする際に、プリフェッチされたルートセグメントと訪問したルートのReact Server Component Payloadがキャッシュに保存されます。
これにより、ナビゲーション時にキャッシュが可能な限り再利用され、新たにサーバーへのリクエストを行う代わりに、パフォーマンスが向上し、リクエスト数と転送データ量が削減されます。
Router Cacheの仕組みと設定方法について詳しく学びましょう。
4. 部分的レンダリング
部分的レンダリングとは、ナビゲーション時に変更されたルートセグメントのみがクライアントで再レンダリングされ、共有セグメントが保持されることを意味します。
たとえば、2つの兄弟ルート/dashboard/settings
と/dashboard/analytics
間をナビゲートする場合、settings
ページはアンマウントされ、analytics
ページは新しい状態でマウントされ、共有されたdashboard
レイアウトは保持されます。この動作は、同じ動的セグメント上の2つのルート間でも存在します。たとえば、/blog/[slug]/page
で/blog/first
から/blog/second
にナビゲートする場合です。
部分的レンダリングがない場合、各ナビゲーションはクライアントでページ全体を再レンダリングします。変更されたセグメントのみをレンダリングすることで、転送されるデータ量と実行時間が削減され、パフォーマンスが向上します。
5. ソフトナビゲーション
ブラウザはページ間のナビゲーション時に「ハードナビゲーション」を行います。Next.js App Routerはページ間の「ソフトナビゲーション」を可能にし、変更されたルートセグメントのみが再レンダリングされるようにします(部分的レンダリング)。これにより、ナビゲーション中にクライアントのReact状態が保持されます。
6. 戻ると進むナビゲーション
デフォルトで、Next.jsは戻ると進むナビゲーションのスクロール位置を維持し、Router Cache内のルートセグメントを再利用します。
7. pages/
とapp/
間のルーティング
pages/
からapp/
へのインクリメンタルな移行時に、Next.jsルーターは自動的に両者間のハードナビゲーションを処理します。pages/
からapp/
への遷移を検出するために、クライアントルーターのフィルターがアプリルートの確率的チェックを利用し、時折誤検出が発生することがあります。デフォルトでは、そのような発生は非常にまれであるべきで、誤検出の可能性を0.01%に設定しています。この可能性はnext.config.js
のexperimental.clientRouterFilterAllowedRate
オプションでカスタマイズできます。誤検出率を下げると、クライアントバンドルで生成されるフィルターのサイズが増加することに注意してください。
また、pages/
とapp/
間のルーティングを完全に無効にし、手動で管理したい場合は、next.config.js
でexperimental.clientRouterFilter
をfalseに設定できます。この機能が無効になっている場合、appルートと重複するpagesの動的ルートはデフォルトで適切にナビゲートされません。