Next.js の App Router で「今どのページを開いているか」や「URL に付いた ?page=2 のようなクエリ文字列」を知りたい場面はよくあります。ナビゲーションで現在地のリンクを強調したり、クエリで絞り込みの状態を管理したりするときに必要です。これを担うのが usePathname と useSearchParams という2つのフックです。この記事では、それぞれの基本的な使い方、Client Component でしか使えない理由、そして useRouter と組み合わせてクエリを書き換える方法まで、初心者〜中級者向けに解説します。
これらのフックは、いずれも next/navigation からインポートして使います。App Router 向けのフックで、Pages Router 時代の next/router とは別物である点に注意してください。
目次
usePathname で現在のパスを取得する
usePathname は、現在表示しているページのパス名(/blog や /products/shoes など、ドメインとクエリを除いた部分)を文字列で返します。よくある使い道が、ナビゲーションで「今いるページのリンク」を強調表示することです。
"use client";
import { usePathname } from "next/navigation";
import Link from "next/link";
const links = [
{ href: "/", label: "ホーム" },
{ href: "/blog", label: "ブログ" },
{ href: "/about", label: "About" },
];
export default function Nav() {
const pathname = usePathname(); // 例: "/blog"
return (
<nav>
{links.map((link) => (
<Link
key={link.href}
href={link.href}
// 現在のパスと一致するリンクを強調
style={{ fontWeight: pathname === link.href ? "bold" : "normal" }}
>
{link.label}
</Link>
))}
</nav>
);
}
usePathname() で現在のパスを取得し、リンクの href と一致するものだけ太字にしています。ページを移動すると pathname が自動的に更新されるため、常に現在地が強調されます。パスだけが必要でクエリは関係ないので、こうした「現在地の判定」には usePathname がぴったりです。
useSearchParams でクエリ文字列を取得する
useSearchParams は、URL のクエリ文字列(? 以降)を読み取るためのフックです。返ってくるのはブラウザ標準の URLSearchParams と同じ操作ができるオブジェクトで、get メソッドで個々の値を取り出せます。
"use client";
import { useSearchParams } from "next/navigation";
// 例として URL が /products?category=shoes&page=2 のとき
export default function ProductList() {
const searchParams = useSearchParams();
const category = searchParams.get("category"); // "shoes"
const page = searchParams.get("page") ?? "1"; // "2"(無ければ "1")
return (
<div>
<p>カテゴリー: {category ?? "すべて"}</p>
<p>ページ: {page}</p>
</div>
);
}
searchParams.get("category") のようにキーを指定して値を取り出します。該当するキーが無いときは null が返るので、?? "1" のようにデフォルト値を用意しておくと安全です。フィルターの条件やページ番号を URL に持たせておくと、その URL を共有・ブックマークするだけで同じ絞り込み状態を再現できるのが利点です。よく使うメソッドを整理します。
| メソッド | 説明 |
|---|---|
get(key) | 指定キーの値を取得する(無ければ null) |
getAll(key) | 同名キーの値をすべて配列で取得する |
has(key) | 指定キーが存在するかを真偽値で返す |
toString() | クエリ文字列全体を文字列で返す |
クエリを書き換えてページ遷移する
useSearchParams は読み取り専用です。クエリを変更したいときは、現在のクエリをもとに新しい文字列を組み立て、useRouter の push(や replace)で遷移します。次はページ番号を切り替える例です。
"use client";
import { usePathname, useSearchParams, useRouter } from "next/navigation";
export default function Pager() {
const pathname = usePathname();
const searchParams = useSearchParams();
const router = useRouter();
function goToPage(page: number) {
// 既存のクエリをコピーして page だけ差し替える
const params = new URLSearchParams(searchParams.toString());
params.set("page", String(page));
// 例: /products?category=shoes&page=3 へ遷移
router.push(`${pathname}?${params.toString()}`);
}
return (
<div>
<button onClick={() => goToPage(1)}>1ページ目</button>
<button onClick={() => goToPage(2)}>2ページ目</button>
</div>
);
}
new URLSearchParams(searchParams.toString()) で現在のクエリをコピーし、set で page だけを書き換えています。こうすると category などの他の条件を残したまま、ページ番号だけを更新できます。組み立てた文字列を usePathname で得たパスとつなげ、router.push で遷移します。履歴を残したくない場合は push の代わりに router.replace を使います。
これらのフックが使えないときの原因
Client Component で使う必要がある
usePathname・useSearchParams・useRouter はいずれもクライアント側で動くフックです。そのため、ファイルの先頭に "use client" を書いた Client Component 内でのみ使えます。Server Component でうっかり呼び出すとエラーになります。なお Server Component では、ページの引数として渡ってくる searchParams プロパティからクエリを受け取れるので、サーバー側で読みたい場合はそちらを使います。
useSearchParams は Suspense 境界が必要になる
useSearchParams を使うコンポーネントは、静的生成との兼ね合いで Suspense の内側に置くことが推奨されます。ビルド時に「Suspense 境界で囲むように」という趣旨の警告やエラーが出た場合は、そのコンポーネントを <Suspense fallback={...}> で包むと解消できます。クエリはリクエストごとに変わり得る値なので、その部分だけを切り離せるようにしておく、という考え方です。
まとめ
usePathname は現在表示しているページのパスを、useSearchParams は URL のクエリ文字列を取得するためのフックで、どちらも next/navigation からインポートして Client Component で使います。usePathname はナビゲーションの現在地判定に、useSearchParams はフィルターやページ番号など URL に状態を持たせる用途に向いています。クエリを書き換えたいときは useSearchParams の値をもとに URLSearchParams を組み立て、useRouter の push で遷移します。Server Component では使えず、useSearchParams は Suspense 境界が求められる点に注意して活用してみてください。