# Navigation (/ui/components/react/navigation)



<Playground component="navigation" />

## Usage [#usage]

```tsx
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'
```

```tsx
<Navigation aria-label="Main" activeLink="dashboard">
  <NavigationList>
    <NavigationItem>
      <NavigationLink href="/dashboard" value="dashboard">
        Dashboard
      </NavigationLink>
    </NavigationItem>
    <NavigationItem>
      <NavigationLink href="/projects" value="projects">
        Projects
      </NavigationLink>
    </NavigationItem>
    <NavigationItem>
      <NavigationLink href="/settings" value="settings">
        Settings
      </NavigationLink>
    </NavigationItem>
  </NavigationList>
</Navigation>
```

`Navigation` is a compound component. The root is a `<nav>` landmark that shares `variant`, `size`, and `orientation` with every link through context, and tracks which link is current via `activeLink`. The parts live under `@appica/ui-react/navigation`:

* **`Navigation`** — the `<nav>` landmark and styling context. Give it an `aria-label`.
* **`NavigationList`** — the `<ul>` that lays the links out as a row (or column).
* **`NavigationItem`** — one `<li>` wrapper.
* **`NavigationLink`** — a link. By default an `<a>`; mark it `active` (or give it a `value` matching the root's `activeLink`) to highlight the current section. Project it onto a router link with `render`.

Set the current item in one of two ways: pass `activeLink` on the root and a matching `value` on each link (handy when a route maps to a key), or set `active` directly on the one current link. A per-link `active` always wins over the root's `activeLink`.

Reach for `Navigation` to move between **distinct pages or sections**. To swap panels within a single page, use [`Tabs`](/ui/components/react/tabs); for navigation that opens rich link panels on hover, use a [`Navigation Menu`](/ui/components/react/navigation-menu); for the trail showing where you are, use a [`Breadcrumb`](/ui/components/react/breadcrumb).

## Examples [#examples]

### Basic [#basic]

A horizontal row of links with the current section highlighted. Each `NavigationLink` carries a `value`; the root's `activeLink` picks the matching one and stamps it with `aria-current="page"`.

```tsx
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'

export default function NavigationBasic() {
  return (
    <Navigation aria-label="Main" activeLink="dashboard" className="max-w-full">
      <NavigationList className="scrollbar-none overflow-x-auto [&::-webkit-scrollbar]:hidden">
        <NavigationItem>
          <NavigationLink href="#!" value="dashboard">
            Dashboard
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink href="#!" value="projects">
            Projects
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink href="#!" value="team">
            Team
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink href="#!" value="settings">
            Settings
          </NavigationLink>
        </NavigationItem>
      </NavigationList>
    </Navigation>
  )
}
```

### Variants [#variants]

`variant` sets how the active and hovered link is drawn. `pill` (default) fills a rounded background; `line` slides an underline in from the side. Both animate, and respect reduced-motion.

```tsx
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'

const ITEMS = [
  { value: 'overview', label: 'Overview' },
  { value: 'analytics', label: 'Analytics' },
  { value: 'reports', label: 'Reports' },
  { value: 'notifications', label: 'Notifications' },
]

export default function NavigationVariants() {
  return (
    <div className="flex w-full max-w-full flex-col items-center gap-8">
      <Navigation aria-label="Pill" variant="pill" activeLink="overview" className="max-w-full">
        <NavigationList className="scrollbar-none overflow-x-auto [&::-webkit-scrollbar]:hidden">
          {ITEMS.map((item) => (
            <NavigationItem key={item.value}>
              <NavigationLink href="#!" value={item.value}>
                {item.label}
              </NavigationLink>
            </NavigationItem>
          ))}
        </NavigationList>
      </Navigation>

      <Navigation aria-label="Line" variant="line" activeLink="overview" className="max-w-full">
        <NavigationList className="scrollbar-none overflow-x-auto [&::-webkit-scrollbar]:hidden">
          {ITEMS.map((item) => (
            <NavigationItem key={item.value}>
              <NavigationLink href="#!" value={item.value}>
                {item.label}
              </NavigationLink>
            </NavigationItem>
          ))}
        </NavigationList>
      </Navigation>
    </div>
  )
}
```

### Sizes [#sizes]

`size` on the root scales the link text, padding, and icon size together — `sm`, `md` (default), or `lg`.

```tsx
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'

const SIZES = ['sm', 'md', 'lg'] as const

const ITEMS = [
  { value: 'home', label: 'Home' },
  { value: 'docs', label: 'Docs' },
  { value: 'pricing', label: 'Pricing' },
]

export default function NavigationSizes() {
  return (
    <div className="flex flex-col gap-6">
      {SIZES.map((size) => (
        <Navigation key={size} aria-label={size} size={size} activeLink="home">
          <NavigationList>
            {ITEMS.map((item) => (
              <NavigationItem key={item.value}>
                <NavigationLink href="#!" value={item.value}>
                  {item.label}
                </NavigationLink>
              </NavigationItem>
            ))}
          </NavigationList>
        </Navigation>
      ))}
    </div>
  )
}
```

### With icons & badges [#with-icons--badges]

Put a leading icon on `data-icon="start"` and a trailing [`Badge`](/ui/components/react/badge) on `data-icon="end"` so the link reserves the right padding. The icon inherits the link's size.

```tsx
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'
import { Badge } from '@appica/ui-react/badge'
import { LayoutDashboard, Folder, Users, Settings } from '@appica/icons-react'

export default function NavigationIcons() {
  return (
    <Navigation aria-label="Main" activeLink="dashboard" className="max-w-full">
      <NavigationList className="scrollbar-none overflow-x-auto [&::-webkit-scrollbar]:hidden">
        <NavigationItem>
          <NavigationLink href="#!" value="dashboard">
            <LayoutDashboard data-icon="start" />
            Dashboard
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink href="#!" value="projects">
            <Folder data-icon="start" />
            Projects
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink href="#!" value="team">
            <Users data-icon="start" />
            Team
            <Badge variant="outline" size="sm" data-icon="end">
              4
            </Badge>
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink href="#!" value="settings">
            <Settings data-icon="start" />
            Settings
          </NavigationLink>
        </NavigationItem>
      </NavigationList>
    </Navigation>
  )
}
```

### Vertical sidebar [#vertical-sidebar]

Set `orientation="vertical"` to stack the links into a sidebar. Links go full-width, the active background or underline reorients, and the padding tightens for a denser column.

```tsx
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'
import { LayoutDashboard, Folder, Users, Settings } from '@appica/icons-react'

const ITEMS = [
  { value: 'dashboard', label: 'Dashboard', Icon: LayoutDashboard },
  { value: 'projects', label: 'Projects', Icon: Folder },
  { value: 'team', label: 'Team', Icon: Users },
  { value: 'settings', label: 'Settings', Icon: Settings },
]

export default function NavigationVertical() {
  return (
    <Navigation aria-label="Sidebar" orientation="vertical" variant="pill" activeLink="projects" className="w-48">
      <NavigationList>
        {ITEMS.map(({ value, label, Icon }) => (
          <NavigationItem key={value}>
            <NavigationLink href="#!" value={value} className="w-full">
              <Icon data-icon="start" />
              {label}
            </NavigationLink>
          </NavigationItem>
        ))}
      </NavigationList>
    </Navigation>
  )
}
```

### Indicator variant [#indicator-variant]

The `indicator` variant — vertical only — hides a chevron that slides in on hover or when active, a common pattern for documentation sidebars. Pass your own marker with the `indicator` prop on a link.

```tsx
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'

const ITEMS = [
  { value: 'getting-started', label: 'Getting started' },
  { value: 'installation', label: 'Installation' },
  { value: 'theming', label: 'Theming' },
  { value: 'components', label: 'Components' },
]

export default function NavigationIndicator() {
  return (
    <Navigation aria-label="Docs" orientation="vertical" variant="indicator" activeLink="theming" className="w-56">
      <NavigationList>
        {ITEMS.map(({ value, label }) => (
          <NavigationItem key={value}>
            <NavigationLink href="#!" value={value}>
              {label}
            </NavigationLink>
          </NavigationItem>
        ))}
      </NavigationList>
    </Navigation>
  )
}
```

### Controlled selection [#controlled-selection]

Drive the active item from state when the highlight follows in-page selection rather than the URL. Project each link onto a `<button>` with `render` and update `activeLink` on click.

```tsx
'use client'

import * as React from 'react'
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'

const ITEMS = [
  { value: 'account', label: 'Account' },
  { value: 'security', label: 'Security' },
  { value: 'billing', label: 'Billing' },
  { value: 'team', label: 'Team' },
]

export default function NavigationControlled() {
  const [active, setActive] = React.useState('account')

  return (
    <Navigation aria-label="Settings" variant="line" activeLink={active} className="max-w-full">
      <NavigationList className="scrollbar-none overflow-x-auto [&::-webkit-scrollbar]:hidden">
        {ITEMS.map(({ value, label }) => (
          <NavigationItem key={value}>
            <NavigationLink value={value} render={<button type="button" onClick={() => setActive(value)} />}>
              {label}
            </NavigationLink>
          </NavigationItem>
        ))}
      </NavigationList>
    </Navigation>
  )
}
```

### Router links [#router-links]

For client-side routing, project a `NavigationLink` onto your framework's link via `render` — here a Next.js [`Link`](https://nextjs.org/docs/app/api-reference/components/link). The link keeps its variant, size, and active styling. Mark an unavailable destination `disabled` to dim it and drop it from the tab order.

```tsx
import Link from 'next/link'
import { Navigation, NavigationList, NavigationItem, NavigationLink } from '@appica/ui-react/navigation'

export default function NavigationLinks() {
  return (
    <Navigation aria-label="Main" activeLink="overview">
      <NavigationList>
        <NavigationItem>
          <NavigationLink value="overview" render={<Link href="/ui/components/react/navigation" />}>
            Overview
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink value="changelog" render={<Link href="/ui/components/react/navigation" />}>
            Changelog
          </NavigationLink>
        </NavigationItem>
        <NavigationItem>
          <NavigationLink value="roadmap" disabled>
            Roadmap
          </NavigationLink>
        </NavigationItem>
      </NavigationList>
    </Navigation>
  )
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

Links lay out from the right, the `line` underline grows from the inline-start edge, and the `indicator` chevron mirrors. For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).

<RtlPreview component="navigation" />

## API reference [#api-reference]

Navigation is a set of styled HTML elements sharing context. Each part forwards every remaining prop to its underlying element (`nav`, `ul`, `li`, `a`), so any native attribute works here — the tables below list the Appica additions and the most common props.

### Navigation [#navigation]

The `<nav>` landmark and styling context.

| Prop          | <ColMinWidth width="210">Type</ColMinWidth>  | Default        | <ColMinWidth width="220">Description</ColMinWidth>                            |
| ------------- | -------------------------------------------- | -------------- | ----------------------------------------------------------------------------- |
| `variant`     | <Code>'pill' \| 'line' \| 'indicator'</Code> | `'pill'`       | Active/hover styling. `indicator` is vertical-only.                           |
| `size`        | <Code>'sm' \| 'md' \| 'lg'</Code>            | `'md'`         | Scales link text, padding, and icons.                                         |
| `orientation` | <Code>'horizontal' \| 'vertical'</Code>      | `'horizontal'` | Lay the links out as a row or a column.                                       |
| `activeLink`  | <Code>string \| number \| null</Code>        | `null`         | The `value` of the current link. Stamps the match with `aria-current="page"`. |
| `aria-label`  | `string`                                     | —              | Accessible name for the navigation landmark.                                  |
| `className`   | `string`                                     | —              | Extra classes, merged via `tailwind-merge`.                                   |

### NavigationList [#navigationlist]

The `<ul>` that arranges the items, with orientation-aware spacing.

| Prop        | Type        | Default | Description                                 |
| ----------- | ----------- | ------- | ------------------------------------------- |
| `children`  | `ReactNode` | —       | The `NavigationItem`s.                      |
| `className` | `string`    | —       | Extra classes, merged via `tailwind-merge`. |

### NavigationItem [#navigationitem]

One link's `<li>` wrapper.

| Prop        | Type        | Default | Description                                 |
| ----------- | ----------- | ------- | ------------------------------------------- |
| `children`  | `ReactNode` | —       | A `NavigationLink`.                         |
| `className` | `string`    | —       | Extra classes, merged via `tailwind-merge`. |

### NavigationLink [#navigationlink]

A navigation link. Built on Base UI's [`useRender`](https://base-ui.com/react/utils/use-render); renders an `<a>` by default. Inherits `variant`, `size`, and `orientation` from the root unless overridden per-link.

| Prop          | <ColMinWidth width="210">Type</ColMinWidth>                 | Default | Description                                                        |
| ------------- | ----------------------------------------------------------- | ------- | ------------------------------------------------------------------ |
| `value`       | <Code>string \| number</Code>                               | —       | Matched against the root's `activeLink` to mark this link current. |
| `active`      | `boolean`                                                   | —       | Force the active state, overriding `activeLink`.                   |
| `disabled`    | `boolean`                                                   | `false` | Dim the link and remove it from the tab order.                     |
| `indicator`   | `ReactNode`                                                 | chevron | Custom marker for the `indicator` variant.                         |
| `variant`     | <Code>'pill' \| 'line' \| 'indicator'</Code>                | root    | Override the root's variant for this link.                         |
| `size`        | <Code>'sm' \| 'md' \| 'lg'</Code>                           | root    | Override the root's size for this link.                            |
| `orientation` | <Code>'horizontal' \| 'vertical'</Code>                     | root    | Override the root's orientation for this link.                     |
| `render`      | <Code>ReactElement \| (props, state) => ReactElement</Code> | —       | Render as your own element (e.g. a router `Link` or a `<button>`). |
| `href`        | `string`                                                    | —       | Navigation target (when rendered as an `<a>`).                     |
| `className`   | `string`                                                    | —       | Extra classes, merged via `tailwind-merge`.                        |

## Accessibility [#accessibility]

* `Navigation` renders a `<nav>` landmark — always give it an `aria-label` (or `aria-labelledby`) so multiple navs on a page are distinguishable.
* The current link is stamped with `aria-current="page"`, announced as the current item without changing its role.
* A vertical navigation sets `data-orientation="vertical"` on the landmark (styling hook — `aria-orientation` isn't valid on `<nav>`).
* `disabled` links set `aria-disabled` and are removed from the tab order.
* Links are ordinary keyboard-focusable anchors (or whatever element you `render`), with a visible focus ring.
* Hover/active transitions are skipped when the user prefers reduced motion.
