Next.js App Routerで現在のパス名を取得する方法
問題の概要
Next.jsのApp Router(app
ディレクトリ)を使用する場合、next/router
からnext/navigation
へルータが変更されました。従来のuseRouter
フックに存在したpathname
プロパティが削除されたため、次のようなエラーが発生します:
Property 'pathname' does not exist on type 'AppRouterInstance'
この問題は、ナビゲーションバーのアクティブなリンクをハイライトしたい場合などに発生します。元のコード例:
import Link from "next/link";
import { useRouter } from 'next/navigation';
const StyledNavLink = ({ to, children }) => {
const router = useRouter();
return (
<Link href={to} className={getNavLinkClass({isActive: router.pathname === to})}>
{children}
</Link>
);
};
App Router環境ではrouter.pathname
が使用できないため、以下の解決策が必要となります。
解決策:クライアントコンポーネントでのパス名取得
クライアント側でパス名を取得するには、next/navigation
が提供するusePathname
フックを使用します。
'use client';
import { usePathname } from 'next/navigation';
import Link from 'next/link';
export function NavLinks() {
const pathname = usePathname();
return (
<nav>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
About
</Link>
</nav>
);
}
重要なポイント
'use client'
ディレクティブが必須(クライアントコンポーネントであることを明示)usePathname
は現在のURLのパス部分のみを返す(クエリパラメータは含まない)- Next.js 13、14、15のすべてのバージョンで動作
ナビゲーションリンクコンポーネントの実装
アクティブなリンクをハイライトする実装例:
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
const StyledNavLink = ({ to, children }) => {
const pathname = usePathname();
const isActive = pathname === to;
return (
<Link
href={to}
className={isActive ? 'text-blue-500 font-bold' : 'text-gray-500'}
>
{children}
</Link>
);
};
解決策:サーバーコンポーネントでのパス名取得
サーバー側でパス名を取得する場合、いくつかのアプローチがあります。
方法1: headers APIを使用する
next/headers
からheaders
メソッドをインポート:
import { headers } from 'next/headers';
export default function Page() {
const headersList = headers();
const activePath = headersList.get('x-invoke-path');
return <div>現在のパス: {activePath}</div>;
}
注意点
x-invoke-path
ヘッダーはNext.jsの内部仕様であるため、将来のバージョンで変更される可能性があります
方法2: ミドルウェアを活用する(より安定した手法)
次のようにミドルウェアを実装:
import { NextResponse } from "next/server";
export function middleware(request) {
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-pathname", request.nextUrl.pathname);
return NextResponse.next({
request: { headers: requestHeaders }
});
}
ミドルウェアで設定したヘッダーをサーバーコンポーネントで取得:
import { headers } from "next/headers";
export default function Page() {
const headersList = headers();
const pathname = headersList.get("x-pathname");
return <div>{pathname}</div>;
}
方法3: ダイナミックルートパラメータを利用
[slug]
を使用するダイナミックルートの場合:
export default function Page({ params }) {
const category = params.category; // パスから抽出
return (
<h1>{category}のページ</h1>
);
}
よくある質問と注意点
Q: サーバーアクション内でパス名を取得できますか?
A: はい、次の方法で取得可能です:
import { pathname } from 'next-extra/pathname';
export async function serverAction() {
const currentPath = pathname();
// 処理を続行
}
ただし、追加ライブラリnext-extra
が必要です:
npm install next-extra
Q: クラインアントとサーバーのどちらを使うべき?
次の基準で選択します:
usePathname
を使う場面:- ユーザーインタラクションに依存する処理
- 状態変化に伴うUI更新
- ナビゲーションリンクのハイライト
サーバーサイド取得を使う場面:
- パス名に基づくデータ取得
- 静的生成パスの決定
- パスに依存するリダイレクト処理
非推奨の方法
URL解析のためにtypeof window !== 'undefined'
やwindow.location
を使用するのは推奨されません:
- サーバーサイドでエラー発生するリスク
- ハイドレーションエラーの原因
- App Routerの設計原則に反する
ベストプラクティスまとめ
クライアントコンポーネントには
usePathname
jsx'use client'; import { usePathname } from 'next/navigation';
サーバーコンポーネントには
headers
APIjsximport { headers } from 'next/headers'; const pathname = headers().get('x-invoke-path');
ダイナミックルートではパラメータ活用
jsxexport default function Page({ params }) { return <h1>{params.slug}</h1>; }
ミドルウェアでカスタムヘッダー設定
jsrequestHeaders.set("x-pathname", request.nextUrl.pathname);
ナビゲーションリンクの状態管理パターン
jsxconst pathname = usePathname(); const isActive = pathname === to;
Next.jsのApp Router環境では、これらの手法を用いることで、クライアント・サーバー双方で現在のパス名を効果的に取得できます。公式ドキュメントのアップデートにも注意しながら、最新のベストプラクティスを適用してください。