Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input OTP
- Input Group
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
- New Component
Elements
Charts
Icons
import * as React from "react";
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, navigationMenuTriggerStyle } from "@/components/ui/navigation-menu";
import { cn } from "@/lib/utils";
import { gettingStarted } from "@/data/navigation-menu";
const ListItem = ({
className,
title,
children,
href,
...props
}: React.ComponentProps<"a"> & { title: string }) => (
<li>
<NavigationMenuLink asChild>
<a
href={href}
className={cn(
"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground block space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors select-none",
className,
)}
{...props}
>
<div className="text-sm font-medium leading-none">{title}</div>
{children && (
<p className="text-muted-foreground line-clamp-2 text-sm leading-snug">{children}</p>
)}
</a>
</NavigationMenuLink>
</li>
);
export const NavigationMenuDemo = () => (
<NavigationMenu>
<NavigationMenuList>
{/* Trigger with content panel */}
<NavigationMenuItem>
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid gap-2 p-4 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
<li className="row-span-3">
<NavigationMenuLink asChild>
<a
className="from-muted/50 to-muted flex h-full w-full flex-col justify-end rounded-md bg-gradient-to-b p-6 no-underline outline-none select-none focus:shadow-md"
href="/"
>
<div className="mt-4 mb-2 text-lg font-semibold">GIN Design</div>
<p className="text-muted-foreground text-sm leading-tight">A custom component registry built on shadcn/ui.</p>
</a>
</NavigationMenuLink>
</li>
{gettingStarted.map((item) => (
<ListItem key={item.title} href={item.href} title={item.title}>{item.description}</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
{/* Trigger with simple grid */}
<NavigationMenuItem>
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-2 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px]">
{[
{ href: "/docs/components/button", title: "Button" },
{ href: "/docs/components/dialog", title: "Dialog" },
{ href: "/docs/components/dropdown-menu", title: "Dropdown Menu" },
{ href: "/docs/components/input", title: "Input" },
{ href: "/docs/components/select", title: "Select" },
{ href: "/docs/components/tooltip", title: "Tooltip" },
].map((item) => (
<ListItem key={item.title} href={item.href} title={item.title} />
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
{/* Plain link — no content panel */}
<NavigationMenuItem><NavigationMenuLink href="/docs" className={navigationMenuTriggerStyle()}>Documentation</NavigationMenuLink></NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
);Plain link
A NavigationMenuItem can hold a plain NavigationMenuLink instead of a trigger. Apply navigationMenuTriggerStyle() to match the trigger appearance.
import {
NavigationMenuItem,
NavigationMenuLink,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
<NavigationMenuItem>
<NavigationMenuLink href="/docs" className={navigationMenuTriggerStyle()}>
Documentation
</NavigationMenuLink>
</NavigationMenuItem>With content panel
Use NavigationMenuTrigger + NavigationMenuContent for a flyout panel. Content is rendered inside the shared NavigationMenuViewport at the bottom of the menu.
<NavigationMenuItem>
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-2 p-4 md:grid-cols-2">
<ListItem href="/docs/components/button" title="Button">
Trigger actions and navigation.
</ListItem>
<ListItem href="/docs/components/dialog" title="Dialog">
Overlay for focused tasks.
</ListItem>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>Featured panel
Combine a full-height NavigationMenuLink with a grid of list items for a featured link layout.
<NavigationMenuContent>
<ul className="grid gap-2 p-4 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
<li className="row-span-3">
<NavigationMenuLink asChild>
<a
className="from-muted/50 to-muted flex h-full w-full flex-col justify-end rounded-md bg-gradient-to-b p-6 no-underline outline-none focus:shadow-md"
href="/"
>
<div className="mt-4 mb-2 text-lg font-semibold">GIN Design</div>
<p className="text-muted-foreground text-sm leading-tight">
A custom component registry built on shadcn/ui.
</p>
</a>
</NavigationMenuLink>
</li>
<ListItem href="/docs/installation" title="Installation">
How to install and configure GIN Design.
</ListItem>
</ul>
</NavigationMenuContent>Without viewport
Pass viewport={false} to opt out of the animated viewport container and render content inline. Useful when you need full control over positioning.
<NavigationMenu viewport={false}>
<NavigationMenuList>...</NavigationMenuList>
</NavigationMenu>navigationMenuTriggerStyle
An exported cva helper that applies the standard trigger appearance to any element — useful for wrapping <Link> from Next.js.
import Link from "next/link";
import { navigationMenuTriggerStyle } from "@/components/ui/navigation-menu";
<NavigationMenuLink asChild>
<Link href="/docs" className={navigationMenuTriggerStyle()}>
Documentation
</Link>
</NavigationMenuLink>Props
NavigationMenu
| Prop | Type | Default | Description |
|---|---|---|---|
viewport | boolean | true | Whether to render the shared NavigationMenuViewport |
NavigationMenuTrigger
ExtendsNavigationMenuPrimitive.Trigger. Renders a <button> with an auto-rotating chevron. data-[state=open] applies when the panel is visible.
NavigationMenuContent
The flyout panel. Slides in from the direction of the previously active item (data-motion animations). md:absolute so it renders inline on mobile.
NavigationMenuLink
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto child element (e.g. <a>, <Link>) |
active | boolean | false | Marks the link as the current page (data-active) |
Usage
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[300px] gap-2 p-4">
<ListItem href="/products/design" title="Design">
Tools for building design systems.
</ListItem>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="/pricing" className={navigationMenuTriggerStyle()}>
Pricing
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>Specs
# Mint — Navigation Menu Specs
## Component
- name: navigation-menu
- file: registry/mint/navigation-menu.tsx
- installs to: components/ui/navigation-menu.tsx
- dependencies: radix-ui, class-variance-authority, lucide-react
- registryDependencies: none
## What is it
A horizontal navigation bar where each item is either a plain link or a trigger
that opens a rich flyout panel. All open panels share a single animated viewport
container so transitions between items are smooth and directional. Use it for
site-level navigation; do not use it for in-page tab switching — prefer Tabs for that.
## Visual identity
Clean, borderless trigger buttons sit flush on --background; a soft mint
accent tint on hover and open state gives subtle feedback without heavy
chrome. The shared viewport floats on --popover with a thin --border and
shadow-lg, creating clear elevation with Mint's signature green warmth.
## Design tokens
--background: oklch(1 0 0) / oklch(0.145 0 0) — trigger resting surface
--accent: oklch(0.92 0.05 155) / oklch(0.26 0.05 155) — hover / focus / open tint on triggers and links
--accent-foreground: oklch(0.32 0.1 155) / oklch(0.88 0.06 155) — text on accent tint
--popover: oklch(1 0 0) / oklch(0.269 0 0) — viewport panel background
--popover-foreground: oklch(0.145 0 0) / oklch(0.985 0 0) — text inside viewport
--border: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%) — viewport border + indicator diamond border
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — focus-visible ring on triggers and links
--muted-foreground: oklch(0.52 0.06 155) / oklch(0.6 0.06 155) — SVG icons inside links (non-text class)
## Sub-components
- NavigationMenu → Root wrapper; max-w-max flex; conditionally appends NavigationMenuViewport; accepts viewport prop
- NavigationMenuList → flex gap-1 list-none row; holds all NavigationMenuItems
- NavigationMenuItem → relative container; no visual output; wraps Trigger+Content or Link
- NavigationMenuTrigger → h-9 px-4 text-sm font-medium button; appends rotating ChevronDown; uses navigationMenuTriggerStyle
- NavigationMenuContent → flyout panel; directional slide+fade animations driven by data-motion; md:absolute positioning
- NavigationMenuLink → flex-col gap-1 rounded-sm p-3 text-sm anchor; supports asChild and active prop
- NavigationMenuViewport → shared animated container; animates h/w via Radix CSS vars; rounded-md border shadow-lg
- NavigationMenuIndicator → optional animated arrow pointing at active item; bg-border diamond shape
- navigationMenuTriggerStyle → exported cva helper; apply to NavigationMenuLink for plain-link trigger appearance
## States
- hover: bg-accent + text-accent-foreground on triggers and links
- focus: bg-accent + text-accent-foreground on triggers and links
- focus-visible: ring-[3px] ring-ring/50 on triggers and links
- data-[state=open]: bg-accent/50 on trigger (panel is open)
- data-[active]: bg-accent/50 on trigger or link (current page)
- data-[state=open] (viewport): zoom-in-90 + animate-in
- data-[state=closed] (viewport): zoom-out-95 + animate-out
- data-[state=visible] (indicator): fade-in + animate-in
- data-[state=hidden] (indicator): fade-out + animate-out
- disabled: pointer-events-none opacity-50 on triggers
## Conventions
- Use NavigationMenuTrigger + NavigationMenuContent for items with flyout panels; use NavigationMenuLink + navigationMenuTriggerStyle() for plain links
- Wrap Next.js <Link> or <a> via asChild on NavigationMenuLink to preserve router integration
- Pass viewport={false} to NavigationMenu when you need full manual control over panel positioning (bypasses the shared viewport)
- The shared NavigationMenuViewport must remain a direct child of NavigationMenu — do not move it
- Mark the active page link with active={true} on NavigationMenuLink (sets data-[active] styling)
- Keyboard navigation: Tab moves between items, Enter/Space opens trigger, ArrowLeft/Right moves within open content, Escape closes, Home/End jump to first/last item
- Sub-components expose data-slot="navigation-menu", "navigation-menu-list", "navigation-menu-item", "navigation-menu-trigger", "navigation-menu-content", "navigation-menu-link", "navigation-menu-viewport", "navigation-menu-indicator" for CSS targetingInstallation
$ npx shadcn add \https://gin-design.vercel.app/navigation-menu