A navigation link that auto-detects and styles the current route, with prefix matching, external links and a collapsible NavGroup.

Last updated

Show code
<NavLink href="/docs">Docs</NavLink>
<NavLink href="/pricing">Pricing</NavLink>
<NavLink href="/about" active>About</NavLink>

Add

npx @ava-ui/apogee add nav-link

Uses @lucide/astro. NavGroup lives in the same folder and is copied with it.

Usage

---
import NavLink from '@/components/apogee/NavLink/NavLink.astro'
---
<NavLink href="/docs">Docs</NavLink>

The active state is automatic: it compares Astro.url.pathname to href (trailing slashes normalized), so the current page’s link lights up with no extra markup. match="prefix" also lights a section link on its child pages. Pass active to override.

API

@composable — exposed to Content Editors in AVA. Any other native <a> attribute passes through (...rest).

Prop Type Default Description
href string — Link target.
active boolean auto-detected Forces the active state. Default compares the current URL’s pathname to href.
match 'exact' | 'prefix' 'exact' prefix also marks /docs active on /docs/button; / only ever matches itself.
external boolean auto from href Opens in a new tab (target, rel) with a trailing icon and a screen-reader note. Auto-detected for http hrefs; pass false to opt out.
externalLabel string '(opens in a new tab)' Screen-reader text after an external link.
class string — Extra classes, merged last.

States

Show code
<NavLink href="/docs">Inactive</NavLink>
<NavLink href="/docs" active>Active</NavLink>
  • Inactive (default) — muted text, brightens on hover.
  • Active — aria-current="page" and full-contrast text; auto-detected from the URL, or forced via active.
  • Focus — a visible focus-visible ring.

Matching

match="prefix" on a section link. This page is /docs/nav-link, so the /docs link is active and the / link is not:

Show code
<NavLink href="/docs" match="prefix">Docs</NavLink>
<NavLink href="/" match="prefix">Home</NavLink>

An http href opens in a new tab with a trailing icon:

Show code
<NavLink href="https://astro.build">Astro</NavLink>

A collapsible group on native <details>. It opens on load when a child is the current page:

Show code
<NavGroup label="Docs">
<BookOpen slot="start" />
<NavLink href="/docs/nav-link">NavLink</NavLink>
<NavLink href="/docs/navbar">Navbar</NavLink>
</NavGroup>
<NavGroup label="Projects">
<FolderKanban slot="start" />
<NavLink href="#">Spring launch</NavLink>
</NavGroup>
Prop Type Default Description
label string — The summary row’s text.
open boolean auto Force open or closed; by default the group opens when it contains the current page.
class string — Extra classes on the <details>.

Slots

start and end take an icon, badge or count. end is pushed to the far edge when the link fills its width (a sidebar) and sits inline in a horizontal navbar.

Show code
<NavLink href="#"><Inbox slot="start" /> Inbox <Badge slot="end" size="small">12</Badge></NavLink>

Examples

A link row, built for use inside Navbar’s default slot:

<NavLink href="/docs">Docs</NavLink>
<NavLink href="/pricing">Pricing</NavLink>
<NavLink href="/blog">Blog</NavLink>

Accessibility

  • Real <a> — full keyboard + AT support for free.
  • aria-current="page" is set on the active link, so assistive tech announces it as the current page (not just visually distinct).
  • Focus ring is always visible on keyboard focus.

Tokens

Token Used for
--foreground-neutral Active link text
--foreground-neutral-faded Inactive link text
--outline-color-focus Focus ring

Do / Don’t

  • Do let the active state auto-detect — only pass active when the route can’t be matched by pathname (e.g. a hash-based section link).
  • Do use NavLink for site navigation, inside Navbar or a Sidebar.
  • Don’t use NavLink for an action — use Button.
  • Don’t hard-code the active state on every page; that defeats the point of auto-detection and drifts out of sync.

Content

  • Labels are nouns, sentence case, one or two words (“Docs”, “Pricing”).
  • No ending punctuation.