# Nav Tabs

> React navigation tabs that are links: the sections of a page or a record change the URL, with the look of tabs and the semantics of navigation.

- Type: React component (Navigation)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add nav-tabs`
- Docs: https://wingo-ui.com/components/nav-tabs

## Interaction

Click a tab and the URL changes: the indicator glides to it on a smooth spring and the page below swaps. Every tab is a real link in the tab order (middle click opens it in a new tab); with focus on a tab, Left / Right move to the neighbour and Home / End jump to the first and last, Enter follows the link. Too many tabs scroll sideways with soft edge fades and the active one is kept in view, or, with overflow set to menu, the ones that do not fit fold into a More menu that becomes a bottom sheet on a phone. On touch every tab is 44px tall. Sticky tabs stay pinned under the top bar on a glass background while the page scrolls.

## Installation

```bash
npx wingo-ui@latest add nav-tabs
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/nav-tabs` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/nav-tabs.tsx`

npm dependencies: `npm i motion lucide-react`

Wingo UI dependencies (installed automatically):

- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))
- [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu) ([Markdown](https://wingo-ui.com/components/dropdown-menu.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useOverflowItems](https://wingo-ui.com/components/use-overflow-items) ([Markdown](https://wingo-ui.com/components/use-overflow-items.md))
- [useScrollEdges](https://wingo-ui.com/components/use-scroll-edges) ([Markdown](https://wingo-ui.com/components/use-scroll-edges.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
"use client"

import { usePathname } from "next/navigation"
import Link from "next/link"
import { NavTabs, type NavTabsItem } from "@/components/ui/nav-tabs"
import { LinkProvider } from "@/lib/navigation"

const sections: NavTabsItem[] = [
  { label: "General", href: "/settings/general" },
  { label: "Members", href: "/settings/members", badge: 8 },
  { label: "Billing", href: "/settings/billing" },
  { label: "Integrations", href: "/settings/integrations" },
  { label: "Notifications", href: "/settings/notifications" },
  { label: "Security", href: "/settings/security" },
]

export function SettingsTabs() {
  return (
    <LinkProvider component={Link} currentPath={usePathname()}>
      <NavTabs items={sections} sticky labels={{ navigation: "Company settings" }} />
    </LinkProvider>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `items?` | `NavTabsItem[]` |  | The tabs (NavItem from lib/navigation): label, href, icon, activeIcon, badge (a number renders a count), disabled, external, match, onSelect. Children are ignored. Items without href render buttons that call onSelect. |
| `value?` | `string` |  | The active tab's key (id, else href, else the label). Controls the tabs; otherwise the current path decides. |
| `defaultValue?` | `string` |  | The first active tab when no path is known (no LinkProvider, no currentPath). Default: the first enabled item. |
| `onValueChange?` | `(key: string, item: NavTabsItem) => void` |  | Runs on every activation, in all three modes (controlled, routed, uncontrolled). |
| `currentPath?` | `string` | `""` | The current pathname. Empty: the LinkProvider's. The tab whose href matches it most specifically is active and clicks store nothing. Try /setari/securitate. |
| `variant?` | `"underline" \| "pill" \| "segmented"` | `"underline"` | underline for page sections, pill for lighter filters, segmented for a compact switch. Class for class the tabs looks. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | 32 / 40 / 48px tabs with the matching type and icon size. On touch md grows to 44px and sm keeps its look with a 44px hit area. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The indicator color: the underline bar, the pill fill or the segmented thumb. Neutral is black in light mode and white in dark mode. |
| `color?` | `string` | `""` | Any CSS color for the indicator; replaces the tone. Leave empty to use the tone. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | The pill and segmented shapes, scaled with the size (md is 8 / 10 / 12px). The underline bar is always round. |
| `bordered?` | `boolean` | `true` | Underline only: the hairline baseline under the whole row. |
| `fullWidth?` | `boolean` | `false` | Tabs share the width equally. The usual choice for two to four segments on a phone. |
| `align?` | `"start" \| "center"` | `"start"` | Where the tabs sit when they are narrower than the row. |
| `sticky?` | `boolean` | `false` | Sticks to the top of the scrolling area under the top bar (top: var(--topbar-offset, 0px), which the topbar writes) on a glass background. |
| `overflow?` | `"scroll" \| "menu"` | `"scroll"` | What happens when the tabs do not fit. scroll: the row scrolls sideways with edge fades and keeps the active tab in view. menu: the last tabs fold into a More menu (a bottom sheet on phones); the active tab never folds. |
| `moreLabel?` | `string` | `"More"` | The label of the overflow trigger and the accessible title of its bottom sheet on phones. |
| `linkComponent?` | `(props: LinkComponentProps) => ReactNode` |  | The router link for this instance (next/link's Link). Default: the LinkProvider's, else a plain anchor. |
| `labels?` | `{ navigation?: string }` |  | The nav landmark's accessible name, default "Sections". An aria-label or aria-labelledby on the component wins. |
| `classNames?` | `Partial<Record<"list" \| "link" \| "indicator" \| "badge" \| "icon" \| "more", string>>` |  | Extra classes for the inner parts: the list, every link, the indicator, badge and icon wrappers, the More trigger. |
| `className?` | `string` |  | Extra classes for the nav root, merged last. Set --bar-bg here when sticky tabs sit on a surface other than the page. |

## Examples

- **Variants**: Underline, pill and segmented: the tabs looks with link semantics.
- **More menu**: overflow="menu" in a narrow card: tabs that do not fit fold into a menu, a sheet on phones.
- **Sticky under a top bar**: Scroll the frame: the tabs pin under the 56px bar through --topbar-offset.
- **Icons and live badges**: Add a message and the count animates; opening the tab clears it.
- **Segmented on a phone**: fullWidth segmented tabs as a filter at the top of a phone screen.
- **Custom color**: Any CSS color for the underline and the active label.

## See also

- [Tabs](https://wingo-ui.com/components/tabs) ([Markdown](https://wingo-ui.com/components/tabs.md))
- [Segmented Control](https://wingo-ui.com/components/segmented-control) ([Markdown](https://wingo-ui.com/components/segmented-control.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [useOverflowItems](https://wingo-ui.com/components/use-overflow-items) ([Markdown](https://wingo-ui.com/components/use-overflow-items.md))

---

Canonical: https://wingo-ui.com/components/nav-tabs
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
