NavLink
Wrapper de lien de navigation qui détecte et met en évidence automatiquement l'état actif basé sur l'URL courante.
Installation
npx shadcn@latest add https://ui.oward.dev/r/registry.json navlinkIntroduction
Dans la plupart des applications React, gérer l'état actif des liens de navigation nécessite d'utiliser des hooks comme usePathname() ou useLocation() dans chaque composant, puis d'écrire de la logique de comparaison d'URL pour appliquer les styles appropriés. Cela devient vite répétitif et difficile à maintenir.
NavLink résout ce problème en centralisant toute cette logique dans un composant réutilisable. Il détecte automatiquement si le lien correspond à l'URL actuelle et applique les classes CSS appropriées, sans que vous ayez à écrire de hooks ou de logique conditionnelle.
La détection se fait côté client après le rendu, ce qui permet de fonctionner correctement avec des composants comme Link de next-intl ou tout autre système qui réécrit les URLs. Le composant analyse l'URL finale rendue dans le DOM, garantissant ainsi une correspondance précise quelle que soit la bibliothèque de routing utilisée.
Cela améliore considérablement l'expérience développeur tout en garantissant la cohérence du comportement de navigation dans toute votre application.
Utilisation
Le composant NavLink enveloppe n'importe quel élément de lien (Link de Next.js, React Router, etc.) et applique automatiquement une classe CSS lorsque l'URL correspond au lien.
Exemple de base
import { NavLink } from "@/ui/navlink";
import Link from "next/link";
export default function Navigation() {
return (
<nav className="flex gap-4">
<NavLink currentClassName="text-blue-600 font-semibold">
<Link href="/about">À propos</Link>
</NavLink>
<NavLink currentClassName="text-blue-600 font-semibold">
<Link href="/contact">Contact</Link>
</NavLink>
</nav>
);
}Props
| Prop | Type | Défaut | Description |
|---|---|---|---|
current | "auto" | "exact" | boolean | "auto" | Mode de correspondance de l'URL |
currentClassName | string | - | Classe CSS à appliquer lorsque le lien est actif |
matchHash | boolean | false | Inclure le hash dans la correspondance d'URL |
matchQuery | boolean | false | Inclure les paramètres de requête dans la correspondance d'URL |
className | string | - | Classes CSS supplémentaires à appliquer |
Modes de correspondance
Mode "auto" (par défaut)
Correspond si l'URL actuelle commence par le href du lien.
<NavLink current="auto" currentClassName="text-blue-600">
<Link href="/docs">Documentation</Link>
</NavLink>| URL actuelle | href du lien | Actif ? |
|---|---|---|
/docs | /docs | ✅ Oui |
/docs/installation | /docs | ✅ Oui |
/documentation | /docs | ❌ Non |
/ | / | ✅ Oui |
/home | / | ✅ Oui |
/about | / | ✅ Oui |
Mode "exact"
Correspond uniquement si l'URL actuelle est exactement identique au href du lien.
<NavLink current="exact" currentClassName="text-blue-600">
<Link href="/docs">Documentation</Link>
</NavLink>| URL actuelle | href du lien | Actif ? |
|---|---|---|
/docs | /docs | ✅ Oui |
/docs/installation | /docs | ❌ Non |
/docs?tab=api | /docs | ❌ Non |
Mode boolean
Force l'état actif/inactif manuellement, ignorant l'URL.
<NavLink current={isActive} currentClassName="text-blue-600">
<Link href="/profile">Profil</Link>
</NavLink>Options avancées
Inclure le hash dans la correspondance
Par défaut, le hash est ignoré. Activez cette option si vous voulez que chaque ancre soit considérée comme une route distincte.
<NavLink matchHash currentClassName="font-bold">
<Link href="/docs#api">Documentation</Link>
</NavLink>| URL actuelle | href du lien | Par défaut (matchHash=false) | matchHash={true} |
|---|---|---|---|
/docs#api | /docs#api | ✅ Actif | ✅ Actif |
/docs#intro | /docs#api | ✅ Actif | ❌ Inactif |
Inclure les paramètres de requête dans la correspondance
Par défaut, les paramètres de requête sont ignorés. Activez cette option si vous voulez que chaque combinaison de paramètres soit considérée comme une route distincte.
<NavLink matchQuery currentClassName="font-bold">
<Link href="/products">Produits</Link>
</NavLink>| URL actuelle | href du lien | Par défaut (matchQuery=false) | matchQuery={true} |
|---|---|---|---|
/products | /products | ✅ Actif | ✅ Actif |
/products?category=shoes | /products | ✅ Actif | ❌ Inactif |
Accessibilité
Le composant ajoute automatiquement l'attribut aria-current="page" lorsque le lien est actif, conformément aux standards WCAG 2.1.
// Rendu HTML lorsque actif
<a href="/about" aria-current="page" className="text-blue-600">
À propos
</a>Performance
Le composant utilise un système de suivi de localisation global partagé entre toutes les instances de NavLink pour minimiser les re-renders :
- Sur les navigateurs modernes, il utilise la Navigation API .
- Sur les anciens navigateurs, un polling léger (250ms) est utilisé en fallback.
Ce fallback sera supprimé dans une future version.
Exemples pratiques
NavLink est compatible avec le pattern asChild de Radix UI.
Vous pouvez l'envelopper dans des composants comme Button, DropdownMenuItem,
ou tout autre composant supportant asChild pour combiner leurs
styles et comportements.
import { NavLink } from "@/ui/navlink";
import { Button } from "@/ui/button";
import Link from "next/link";
export function MainNav() {
return (
<nav className="flex gap-6">
<Button asChild variant="ghost">
<NavLink currentClassName="text-primary font-semibold border-b-2 border-primary">
<Link href="/">Accueil</Link>
</NavLink>
</Button>
<Button asChild variant="ghost">
<NavLink currentClassName="text-primary font-semibold border-b-2 border-primary">
<Link href="/contact">Contact</Link>
</NavLink>
</Button>
</nav>
);
}Le composant enfant de NavLink doit être un élément React valide avec une
prop href. En mode développement, une erreur sera levée si
cette condition n'est pas respectée.