1. ホーム
  2. Next.js

【Next.js】Link コンポーネントでページ遷移する方法|useRouter での遷移も解説

Share

Next.js でページ間を移動するときは、next/link<Link> コンポーネントを使うのが基本です。この記事では、なぜ普通の <a> タグではなく <Link> を使うのか、基本的な書き方から動的なパスへのリンク、現在のパスに応じたアクティブ表示、さらにボタンのクリックなどプログラムからページを移動させる useRouter まで、順番に解説します。ここでは Next.js 13 以降で標準になった App Router(app ディレクトリ)を前提にしています。初心者の方がつまずきやすい next/routernext/navigation の違いにも触れるので、遷移まわりで迷ったときの参考にしてください。

なぜ a タグではなく Link を使うのか

ページ内リンクは普通の <a> タグでも書けますが、Next.js では内部のページ移動に <Link> を使うのが基本です。理由は大きく2つあります。1つ目はクライアントサイド遷移です。<a> でページを移動すると、ブラウザがページ全体をサーバーから読み込み直します。一方 <Link> は、必要な部分だけを差し替える形で切り替わるため、ページ全体の再読み込みが起きません。表示が速く、スクロール位置や入力中の状態なども保たれやすくなります。

2つ目はプリフェッチです。<Link> は、そのリンクが画面に表示されると、リンク先のページを裏側で先読みしておきます。そのためユーザーが実際にクリックしたときには、多くの場合すでに準備が済んでいて、体感的にほぼ待ち時間なく切り替わります。外部サイトへのリンクにはこうした仕組みは不要なので、外部リンクはこれまでどおり <a> で書いて問題ありません。<Link> はあくまでサイト内のページ移動に使うもの、と覚えておきましょう。

Link の基本的な使い方

まずは基本の形です。next/link から Linkimport し、href に移動先の URL を渡します。書き方は <a> とほとんど同じで、href にパスを指定し、タグの中にリンクのテキストを書くだけです。

app/page.tsx
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 をパスに差し込んでリンクを生成しています。

app/blog/page.tsx
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}`} の部分がポイントです。バッククォートで囲んだ中に ${ } を書き、その中に変数を入れると、値が文字列に埋め込まれます。id1 のリンクは /blog/12 のリンクは /blog/2 になります。このリンク先を実際のページとして受け取るには、app/blog/[id]/page.tsx のような動的ルート(フォルダ名を角括弧で囲んだページ)を用意します。href に文字列ではなく { pathname, query } のようなオブジェクトを渡す書き方もありますが、まずは上のテンプレートリテラルの形を押さえておけば十分です。

今いるページのリンクを目立たせる

ナビゲーションでは、「今開いているページ」のリンクを色や太さで目立たせたいことがあります。現在の URL のパスは、next/navigationusePathname というフックで取得できます。取得したパスと href を見比べて、一致していればアクティブ用のクラスを付ける、という書き方が定番です。

app/components/Nav.tsx
'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 では、useRouternext/navigation から import する点に注意してください。取得した router オブジェクトの push メソッドに移動先のパスを渡すと、そのページへ移動します。

app/login/page.tsx
'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 になります。useRouterusePathnameonClick といった仕組みはブラウザ側でしか動かないため、Server Component の中でそのまま使うとエラーになります。

解決策は、そのコンポーネントを Client Component にすることです。ファイルの一番上'use client' と書くだけで、そのファイルはブラウザ側でも動くコンポーネントになります。先ほどの useRouterusePathname の例で先頭に 'use client' が付いていたのは、このためです。遷移まわりのフックを使うコンポーネントには 'use client' を忘れないようにしましょう。逆に、<Link> だけを置くコンポーネントは 'use client' が無くても使えます。

もう1つよくある間違いが、next/routernext/navigation の取り違えです。next/router は旧来の Pages Router 用で、App Router では使えません。App Router で useRouterusePathname を使うときは、必ず 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/navigationuseRouter を使う、という使い分けが基本です。<Link> はクライアントサイド遷移とプリフェッチにより表示が速く、href にテンプレートリテラルを使えば動的なパスにも対応できます。現在のパスは usePathname で取得してアクティブ表示に使えます。useRouterusePathname を使うファイルには 'use client' が必要で、import 元は next/router ではなく next/navigation である点にだけ注意すれば、App Router の遷移でつまずくことはほとんどなくなるはずです。

参考ページ