Skip to content

Next.js App Routerで現在のパス名を取得する方法

問題の概要

Next.jsのApp Router(appディレクトリ)を使用する場合、next/routerからnext/navigationへルータが変更されました。従来のuseRouterフックに存在したpathnameプロパティが削除されたため、次のようなエラーが発生します:

Property 'pathname' does not exist on type 'AppRouterInstance'

この問題は、ナビゲーションバーのアクティブなリンクをハイライトしたい場合などに発生します。元のコード例:

js
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フックを使用します。

jsx
'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のすべてのバージョンで動作

ナビゲーションリンクコンポーネントの実装

アクティブなリンクをハイライトする実装例:

jsx
'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メソッドをインポート:

jsx
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: ミドルウェアを活用する(より安定した手法)

次のようにミドルウェアを実装:

js
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 }
  });
}

ミドルウェアで設定したヘッダーをサーバーコンポーネントで取得:

jsx
import { headers } from "next/headers";

export default function Page() {
  const headersList = headers();
  const pathname = headersList.get("x-pathname");
  
  return <div>{pathname}</div>;
}

方法3: ダイナミックルートパラメータを利用

[slug]を使用するダイナミックルートの場合:

jsx
export default function Page({ params }) {
  const category = params.category; // パスから抽出
  return (
    <h1>{category}のページ</h1>
  );
}

よくある質問と注意点

Q: サーバーアクション内でパス名を取得できますか?

A: はい、次の方法で取得可能です:

js
import { pathname } from 'next-extra/pathname';

export async function serverAction() {
  const currentPath = pathname();
  // 処理を続行
}

ただし、追加ライブラリnext-extraが必要です:

bash
npm install next-extra

Q: クラインアントとサーバーのどちらを使うべき?

次の基準で選択します:

  • usePathnameを使う場面:

    • ユーザーインタラクションに依存する処理
    • 状態変化に伴うUI更新
    • ナビゲーションリンクのハイライト
  • サーバーサイド取得を使う場面:

    • パス名に基づくデータ取得
    • 静的生成パスの決定
    • パスに依存するリダイレクト処理

非推奨の方法

URL解析のためにtypeof window !== 'undefined'window.locationを使用するのは推奨されません:

  • サーバーサイドでエラー発生するリスク
  • ハイドレーションエラーの原因
  • App Routerの設計原則に反する

ベストプラクティスまとめ

  1. クライアントコンポーネントには usePathname

    jsx
    'use client';
    import { usePathname } from 'next/navigation';
  2. サーバーコンポーネントには headers API

    jsx
    import { headers } from 'next/headers';
    const pathname = headers().get('x-invoke-path');
  3. ダイナミックルートではパラメータ活用

    jsx
    export default function Page({ params }) {
      return <h1>{params.slug}</h1>;
    }
  4. ミドルウェアでカスタムヘッダー設定

    js
    requestHeaders.set("x-pathname", request.nextUrl.pathname);
  5. ナビゲーションリンクの状態管理パターン

    jsx
    const pathname = usePathname();
    const isActive = pathname === to;

Next.jsのApp Router環境では、これらの手法を用いることで、クライアント・サーバー双方で現在のパス名を効果的に取得できます。公式ドキュメントのアップデートにも注意しながら、最新のベストプラクティスを適用してください。