Next.jsで現在地をハイライトする方法。
ナビゲーションメニューなどでよくある色が変わるもの。
仕組みとしては簡単で、アクセスしたURLを取得し、リンク内のhrefと比較して、同一のものと違うもので適用するスタイルを変えるだけ。
通常ではクラス名を入れ替えるだけで良いが、Tailwind CSSを使う場合はTailwind CSSで設定する色名などを直接変える。
Next.js APP routerバージョン
クライアントコンポーネントになるので、use clientを最初に入れる。
ページのURLを取得するためにusePathnameをインポートする。
ナビゲーションになる配列を準備しておく。
joinStr関数は、Tailwind CSSのクラス名を接続するために使っている。文字列連結はいろいろな方法があるので、好みの手法を使えば良い。
Reactでよくある、map()で繰り返しでHTMLを出力する方法を使う。Reactの仕様で一意のkeyが必要なので、忘れずに設定しておく。
作成しておいたナビゲーション配列にmap()を使い、usePathnameで取得したURLと、ナビゲーション配列のhrefを判定する。
判定結果による出し分けは、三項演算子で行う。
joinStr関数の中で、三項演算子での処理を行い、カンマで区切った後のクラス名は、どちらの場合も反映される共通のスタイルを入れておくと楽。
以下はコンポーネントとして作成した場合。
"use client";
import { usePathname } from "next/navigation";
import Link from "next/link";
const navigation = [
{ id: 1, name: "1番目", href: "/01" },
{ id: 2, name: "2番目", href: "/02" },
{ id: 3, name: "3番目", href: "/03" },
];
const joinStr = (...classes: string[]) => {
return classes.filter(Boolean).join(" ");
};
export function AllNavC() {
const pathname = usePathname();
return (
<div className="pl-[20px]">
<div className="mb-[10px] text-[15px] font-semibold text-[#bebebe]">
メニュー
</div>
{navigation.map(({ href, name }) => (
<Link
href={href}
key={name}
className={joinStr(
pathname === href
? "text-[#b5faff]"
: "text-[#9b9b9b] hover:text-[#bebebe]",
"block pl-[20px] ",
)}
>
{name}
</Link>
))}
</div>
);
}
APPルーター補足「クライアントコンポーネント」
Next.jsがAPPルーターになってから、いろいろと変わっている。
現在地をハイライトする時に良く使う現在のURLを取得するものもusePathnameに変わっている。
このusePathnameは、サーバー側では使えない。考えてみれば当然だが、今アクセスしたURLは、クライアント側で処理をしない限りわからないため。
そのため、usePathnameを使うと、そのコンポーネントはクライアントコンポーネントになる。
Next.jsのAPPルーターでは、何も明示しない場合・親のコンポーネントにuse clientの記述がない場合は、基本はサーバーコンポーネントとして取り扱われる。
use clientと記述が入っていたり、記述が入ったコンポーネントの子のコンポーネントは、クライアントコンポーネントとして取り扱われる。
要はuse clientはここからクライアントコンポーネントですよという区切りの宣言ということらしい。
useStateなどのフックもそうだが、クライアント側でユーザーがアクセスしたときに処理したい何かを入れる場合は、クライアントコンポーネントとして宣言しなければならないというのが、APPルーターになって大きく変わったところで、この現在地ハイライトにも結構大きく影響している。
Next.js pagesルーターバージョン
pagesルーターではuseRouterでURLを取得する。
useRouterで取得したデータrouterからいろいろなデータを取ることができる。router.pathnameで、そのページのパスを取得できるが、Next.jsの動的ルーティングの中身([id]のid部分)を取得することはできない。router.asPathとすることで[id]の中身が取得できる。
それ以外はほぼ同じ。特にTailwind CSSのところは変わりがない。
import { useRouter } from "next/router";
import Link from "next/link";
const navigation = [
{ id: 1, name: "1番目", href: "/01" },
{ id: 2, name: "2番目", href: "/02" },
{ id: 3, name: "3番目", href: "/03" },
];
const joinStr = (...classes: string[]) => {
return classes.filter(Boolean).join(" ");
};
export function AllNavC() {
const router = useRouter();
return (
<div className="pl-[20px]">
<div className="mb-[10px] text-[15px] font-semibold text-[#bebebe]">
メニュー
</div>
{navigation.map(({ href, name }) => (
<Link
href={href}
key={name}
className={joinStr(
router.pathname === href
? "text-[#b5faff]"
: "text-[#9b9b9b] hover:text-[#bebebe]",
"block pl-[20px] ",
)}
>
{name}
</Link>
))}
</div>
);
}
まとめ
いろいろ付け加えることで、様々なことができる。
Tailwind CSSだけの調整でも、アンダーラインを引いたり、背景色も変えたりなど。
また、HTMLを付け加えて、左にボーダー線を出して、現在地だけ左のラインの色が変わるとか。
before afterを使えば様々な装飾もできる。
Tailwind CSS部分は好みの問題なので、その時のデザインで色々変えると良い。
Tailwind CSSを使わないのならば、単純にクラス名を変えるだけなので記述としてはすっきりしてわかりやすくなる。Tailwind CSSは複雑な設定を使用とすると、クラス名が横に大量に並んでしまうので、そこはちょっとめんどくさいところ。
どんなCSSを使うかで、この辺りは変わる。
現在地のハイライト自体の原理は簡単。いろいろと応用も利く。
今のURLを取得して、そこに判定を加えて様々な見た目に変えるというのは、ページネーションのナビゲーションなど、いろんな所で使い道があるので結構便利。

