oward

NavLink

Wrapper de lien de navigation qui détecte et met en évidence automatiquement l'état actif basé sur l'URL courante.

newbeta

Installation

npx shadcn@latest add https://ui.oward.dev/r/registry.json navlink

Introduction

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

PropTypeDéfautDescription
current"auto" | "exact" | boolean"auto"Mode de correspondance de l'URL
currentClassNamestring-Classe CSS à appliquer lorsque le lien est actif
matchHashbooleanfalseInclure le hash dans la correspondance d'URL
matchQuerybooleanfalseInclure les paramètres de requête dans la correspondance d'URL
classNamestring-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 actuellehref du lienActif ?
/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 actuellehref du lienActif ?
/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

<NavLink matchHash currentClassName="font-bold">
  <Link href="/docs#api">Documentation</Link>
</NavLink>
URL actuellehref du lienPar 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

<NavLink matchQuery currentClassName="font-bold">
  <Link href="/products">Produits</Link>
</NavLink>
URL actuellehref du lienPar 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

Exemples pratiques

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