Next.js でページ間を移動するときは、next/link の <Link> コンポーネントを使うのが基本です。この記事では、なぜ普通の <a> タグではなく <Link> を使うのか、基本的な書き方から動的なパスへのリンク、現在のパスに応じたアクティブ表示、さらにボタンのクリックなどプログラムからページを移動させる useRouter まで、順番に解説します。ここでは Next.js 13 以降で標準になった App Router(app ディレクトリ)を前提にしています。初心者の方がつまずきやすい next/router と next/navigation の違いにも触れるので、遷移まわりで迷ったときの参考にしてください。
目次
なぜ a タグではなく Link を使うのか
ページ内リンクは普通の <a> タグでも書けますが、Next.js では内部のページ移動に <Link> を使うのが基本です。理由は大きく2つあります。1つ目はクライアントサイド遷移です。<a> でページを移動すると、ブラウザがページ全体をサーバーから読み込み直します。一方 <Link> は、必要な部分だけを差し替える形で切り替わるため、ページ全体の再読み込みが起きません。表示が速く、スクロール位置や入力中の状態なども保たれやすくなります。
2つ目はプリフェッチです。<Link> は、そのリンクが画面に表示されると、リンク先のページを裏側で先読みしておきます。そのためユーザーが実際にクリックしたときには、多くの場合すでに準備が済んでいて、体感的にほぼ待ち時間なく切り替わります。外部サイトへのリンクにはこうした仕組みは不要なので、外部リンクはこれまでどおり <a> で書いて問題ありません。<Link> はあくまでサイト内のページ移動に使うもの、と覚えておきましょう。
Link の基本的な使い方
まずは基本の形です。next/link から Link を import し、href に移動先の URL を渡します。書き方は <a> とほとんど同じで、href にパスを指定し、タグの中にリンクのテキストを書くだけです。
import Link from 'next/link';
export default function Home() {
return (
<nav>
<Link href="/">ホーム</Link>
<Link href="/about">会社概要</Link>
<Link href="/blog">ブログ</Link>
</nav>
);
}
この <Link> は、最終的にはブラウザ上で <a> タグとして表示されます。つまり、見た目のスタイル(className を付けて CSS を当てるなど)は <a> と同じ感覚で扱えます。古いバージョンの Next.js では <Link> の中に自分で <a> を書く必要がありましたが、現在の App Router 対応版では上のように <Link> の中に直接テキストや要素を書くだけで大丈夫です。
変数を使って動的なパスにリンクする
ブログ記事の一覧のように、「同じ形式で URL の一部だけが違う」リンクをたくさん並べたい場面はよくあります。その場合は、href にテンプレートリテラル(バッククォート)を使って、変数を埋め込んだパスを作ります。次の例は、記事の配列を map で回して、それぞれの id をパスに差し込んでリンクを生成しています。
import Link from 'next/link';
const posts = [
{ id: 1, title: 'はじめての記事' },
{ id: 2, title: 'Next.js に入門する' },
];
export default function BlogList() {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.id}`}>{post.title}</Link>
</li>
))}
</ul>
);
}
href={`/blog/${post.id}`} の部分がポイントです。バッククォートで囲んだ中に ${ } を書き、その中に変数を入れると、値が文字列に埋め込まれます。id が 1 のリンクは /blog/1、2 のリンクは /blog/2 になります。このリンク先を実際のページとして受け取るには、app/blog/[id]/page.tsx のような動的ルート(フォルダ名を角括弧で囲んだページ)を用意します。href に文字列ではなく { pathname, query } のようなオブジェクトを渡す書き方もありますが、まずは上のテンプレートリテラルの形を押さえておけば十分です。
今いるページのリンクを目立たせる
ナビゲーションでは、「今開いているページ」のリンクを色や太さで目立たせたいことがあります。現在の URL のパスは、next/navigation の usePathname というフックで取得できます。取得したパスと href を見比べて、一致していればアクティブ用のクラスを付ける、という書き方が定番です。
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
export default function Nav() {
const pathname = usePathname();
return (
<nav>
<Link
href="/about"
className={pathname === '/about' ? 'active' : ''}
>
会社概要
</Link>
</nav>
);
}
usePathname は現在のパス(たとえば /about)を文字列で返します。上の例では、それが href と一致するときだけ active というクラスを付け、あとは CSS で見た目を変えます。usePathname はブラウザ側で動くフックなので、ファイルの先頭に 'use client' が必要です(この点は次の useRouter でも共通なので、あとで詳しく説明します)。
useRouter でボタンから遷移する
<Link> はクリックで移動するリンク用ですが、「フォームの送信が終わったら一覧へ戻る」「ボタンを押したら次のページへ進む」のように、処理の途中でプログラムからページを移動させたいこともあります。そのときに使うのが useRouter フックです。App Router では、useRouter を next/navigation から import する点に注意してください。取得した router オブジェクトの push メソッドに移動先のパスを渡すと、そのページへ移動します。
'use client';
import { useRouter } from 'next/navigation';
export default function LoginPage() {
const router = useRouter();
const handleLogin = async () => {
// ここでログイン処理などを行う
await new Promise((resolve) => setTimeout(resolve, 500));
// 処理が終わったらダッシュボードへ移動する
router.push('/dashboard');
};
return (
<button onClick={handleLogin}>ログイン</button>
);
}
router.push('/dashboard') を呼ぶと、/dashboard へ移動します。<Link> と同じくクライアントサイド遷移なので、ページ全体の再読み込みは起きません。push のほかにも、ブラウザの「戻る」を行う router.back() や、今のページを再取得する router.refresh() などがよく使われます。
Server Component では useRouter が使えない
ここが初心者のつまずきやすいところです。App Router のコンポーネントは、何も書かなければサーバー側で実行される Server Component になります。useRouter や usePathname、onClick といった仕組みはブラウザ側でしか動かないため、Server Component の中でそのまま使うとエラーになります。
解決策は、そのコンポーネントを Client Component にすることです。ファイルの一番上に 'use client' と書くだけで、そのファイルはブラウザ側でも動くコンポーネントになります。先ほどの useRouter や usePathname の例で先頭に 'use client' が付いていたのは、このためです。遷移まわりのフックを使うコンポーネントには 'use client' を忘れないようにしましょう。逆に、<Link> だけを置くコンポーネントは 'use client' が無くても使えます。
もう1つよくある間違いが、next/router と next/navigation の取り違えです。next/router は旧来の Pages Router 用で、App Router では使えません。App Router で useRouter や usePathname を使うときは、必ず next/navigation から import してください。"useRouter is not defined" のようなエラーや、router.push が期待どおり動かないときは、import 元が next/router になっていないかを最初に確認するとよいです。
Link と useRouter でよく使うオプション
基本の使い方が分かったら、あわせて覚えておくと便利なオプションを紹介します。次の表は、<Link> の属性と router のメソッドのうち、実際によく使うものをまとめたものです。
| オプション / メソッド | 説明 |
|---|---|
<Link href="..."> | 移動先の URL を指定する。必須の属性。 |
<Link replace> | 履歴に追加せず現在のページを置き換える。ブラウザの「戻る」で前のページに戻らなくなる。 |
<Link prefetch={false}> | 先読み(プリフェッチ)を無効にする。既定は自動で先読みされる。 |
<Link scroll={false}> | 遷移後にページ先頭へスクロールするのを止める。 |
router.push('/path') | 指定したパスへ移動し、履歴に追加する。 |
router.replace('/path') | 履歴に追加せずに移動する。<Link replace> のプログラム版。 |
router.back() | ブラウザの「戻る」と同じ動作をする。 |
router.refresh() | 現在のページのデータを取得し直して再描画する。 |
replace は、ログイン後のリダイレクトなど「戻るボタンで前の画面に戻ってほしくない」場面で役立ちます。prefetch は基本は自動でよいですが、リンクが大量にあってすべてを先読みしたくない場合などに false にします。
まとめ
Next.js のページ移動は、リンクとして見せるなら next/link の <Link>、処理の途中でプログラムから移動させるなら next/navigation の useRouter を使う、という使い分けが基本です。<Link> はクライアントサイド遷移とプリフェッチにより表示が速く、href にテンプレートリテラルを使えば動的なパスにも対応できます。現在のパスは usePathname で取得してアクティブ表示に使えます。useRouter や usePathname を使うファイルには 'use client' が必要で、import 元は next/router ではなく next/navigation である点にだけ注意すれば、App Router の遷移でつまずくことはほとんどなくなるはずです。