Shadcn Navbar (Navigation Menu)
Free shadcn/ui navbar built on the Navigation Menu (Radix). Dropdown menus, a simple link bar, a feature dropdown, a mega menu, active links, and a complete responsive navbar with a mobile hamburger Sheet.
Installation
{
"registries": {
"@designrevision": "https://registry.designrevision.com/r/{name}.json"
}
}
Add to your existing components.json. Requires shadcn/ui v2.3+.
Packages
Props
viewport
= true
boolean
Render the shared viewport that animates dropdown size/position. Set false to position each NavigationMenuContent under its own item.
A Radix navbar with dropdown menus — the base for a site nav. Compose <NavigationMenu> → <NavigationMenuList> → <NavigationMenuItem>, each with a <NavigationMenuTrigger> + <NavigationMenuContent> (the dropdown) or a plain <NavigationMenuLink className={navigationMenuTriggerStyle()}>. Hover/focus opens menus, with a shared animated viewport (viewport=false to drop it per-item). Pair with Sheet for a mobile hamburger menu.
import * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority"
import { ChevronDownIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function NavigationMenu({
className,
children,
viewport = true,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
viewport?: boolean
}) {
return (
<NavigationMenuPrimitive.Root
data-slot="navigation-menu"
data-viewport={viewport}
className={cn(
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
className
)}
{...props}
>
{children}
{viewport && <NavigationMenuViewport />}
</NavigationMenuPrimitive.Root>
)
}
function NavigationMenuList({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
return (
<NavigationMenuPrimitive.List
data-slot="navigation-menu-list"
className={cn(
"group flex flex-1 list-none items-center justify-center gap-1",
className
)}
{...props}
/>
)
}
function NavigationMenuItem({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
return (
<NavigationMenuPrimitive.Item
data-slot="navigation-menu-item"
className={cn("relative", className)}
{...props}
/>
)
}
const navigationMenuTriggerStyle = cva(
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
)
function NavigationMenuTrigger({
className,
children,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
return (
<NavigationMenuPrimitive.Trigger
data-slot="navigation-menu-trigger"
className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props}
>
{children}{" "}
<ChevronDownIcon
className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
aria-hidden="true"
/>
</NavigationMenuPrimitive.Trigger>
)
}
function NavigationMenuContent({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
return (
<NavigationMenuPrimitive.Content
data-slot="navigation-menu-content"
className={cn(
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
className
)}
{...props}
/>
)
}
function NavigationMenuViewport({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
return (
<div
className={cn(
"absolute top-full left-0 isolate z-50 flex justify-center"
)}
>
<NavigationMenuPrimitive.Viewport
data-slot="navigation-menu-viewport"
className={cn(
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
className
)}
{...props}
/>
</div>
)
}
function NavigationMenuLink({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
return (
<NavigationMenuPrimitive.Link
data-slot="navigation-menu-link"
className={cn(
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function NavigationMenuIndicator({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
return (
<NavigationMenuPrimitive.Indicator
data-slot="navigation-menu-indicator"
className={cn(
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
className
)}
{...props}
>
<div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
</NavigationMenuPrimitive.Indicator>
)
}
export {
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator,
NavigationMenuViewport,
navigationMenuTriggerStyle,
}
Examples
Dropdown menus
The canonical navigation menu — triggers opening rich dropdown grids, plus a plain link.
Packages
Props
No props documented yet.
import * as React from "react"
import { cn } from "@/lib/utils"
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"
const components = [
{
title: "Alert Dialog",
href: "#",
description:
"A modal dialog that interrupts the user with important content.",
},
{
title: "Hover Card",
href: "#",
description: "For sighted users to preview content behind a link.",
},
{
title: "Progress",
href: "#",
description: "Displays an indicator showing the completion of a task.",
},
{
title: "Tabs",
href: "#",
description: "Layered sections of content shown one panel at a time.",
},
]
function ListItem({
title,
children,
href,
}: {
title: string
children: React.ReactNode
href: string
}) {
return (
<li>
<NavigationMenuLink asChild>
<a
href={href}
className="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"
>
<div className="text-sm leading-none font-medium">{title}</div>
<p className="text-muted-foreground line-clamp-2 text-sm leading-snug">
{children}
</p>
</a>
</NavigationMenuLink>
</li>
)
}
export default function NavigationMenuDemo01() {
return (
<NavigationMenu>
<NavigationMenuList>
<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
href="#"
className="from-muted/50 to-muted flex h-full w-full flex-col justify-end rounded-md bg-linear-to-b p-6 no-underline outline-none select-none focus:shadow-md"
>
<div className="mt-4 mb-2 text-lg font-medium">
shadcn/ui
</div>
<p className="text-muted-foreground text-sm leading-tight">
Beautifully designed components built with Tailwind CSS.
</p>
</a>
</NavigationMenuLink>
</li>
<ListItem href="#" title="Introduction">
Re-usable components built using Radix UI and Tailwind CSS.
</ListItem>
<ListItem href="#" title="Installation">
How to install dependencies and structure your app.
</ListItem>
<ListItem href="#" title="Typography">
Styles for headings, paragraphs, lists, and more.
</ListItem>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-2 p-4 md:w-[500px] md:grid-cols-2">
{components.map((component) => (
<ListItem
key={component.title}
title={component.title}
href={component.href}
>
{component.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild className={cn(navigationMenuTriggerStyle())}>
<a href="#">Docs</a>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)
}
Simple link bar
A flat nav bar of links with no dropdowns — each styled with `navigationMenuTriggerStyle()`.
Packages
Props
No props documented yet.
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"
const links = [
{ label: "Home", href: "#" },
{ label: "Features", href: "#" },
{ label: "Pricing", href: "#" },
{ label: "About", href: "#" },
]
export default function NavigationMenuSimple01() {
return (
<NavigationMenu>
<NavigationMenuList>
{links.map((link) => (
<NavigationMenuItem key={link.label}>
<NavigationMenuLink
asChild
className={navigationMenuTriggerStyle()}
>
<a href={link.href}>{link.label}</a>
</NavigationMenuLink>
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
)
}
Feature dropdown
A product dropdown with icons, titles, and descriptions — the feature-grid pattern.
Packages
Props
No props documented yet.
import { BookOpen, LayoutGrid, LifeBuoy, Puzzle } from "lucide-react"
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from "@/components/ui/navigation-menu"
const features = [
{ icon: LayoutGrid, title: "Dashboard", desc: "Overview of your workspace." },
{ icon: Puzzle, title: "Integrations", desc: "Connect your favorite tools." },
{ icon: BookOpen, title: "Documentation", desc: "Guides and API reference." },
{ icon: LifeBuoy, title: "Support", desc: "Get help from our team." },
]
export default function NavigationMenuIcons01() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Product</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[420px] gap-1 p-2 md:grid-cols-2">
{features.map((feature) => (
<li key={feature.title}>
<NavigationMenuLink asChild>
<a
href="#"
className="hover:bg-accent flex flex-row items-start gap-3 rounded-md p-3"
>
<feature.icon className="text-muted-foreground size-5" />
<div>
<div className="text-sm leading-none font-medium">
{feature.title}
</div>
<p className="text-muted-foreground mt-1 text-sm leading-snug">
{feature.desc}
</p>
</div>
</a>
</NavigationMenuLink>
</li>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)
}
Responsive navbar
A complete site navbar — desktop NavigationMenu plus a hamburger that opens a Sheet on mobile.
Packages
Props
No props documented yet.
import { Menu } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"
import {
Sheet,
SheetContent,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
const links = [
{ label: "Home", href: "#" },
{ label: "Features", href: "#" },
{ label: "Pricing", href: "#" },
{ label: "About", href: "#" },
]
export default function NavigationMenuNavbar01() {
return (
<div className="w-full max-w-3xl">
<header className="flex h-14 items-center justify-between rounded-lg border px-4">
<a href="#" className="flex items-center gap-2 font-semibold">
<div className="bg-primary text-primary-foreground flex size-7 items-center justify-center rounded-md text-sm">
A
</div>
Acme
</a>
<NavigationMenu className="hidden md:flex">
<NavigationMenuList>
{links.map((link) => (
<NavigationMenuItem key={link.label}>
<NavigationMenuLink
asChild
className={navigationMenuTriggerStyle()}
>
<a href={link.href}>{link.label}</a>
</NavigationMenuLink>
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
<div className="flex items-center gap-2">
<Button size="sm" className="hidden md:inline-flex">
Sign up
</Button>
<Sheet>
<SheetTrigger asChild>
<Button variant="outline" size="icon" className="md:hidden">
<Menu />
<span className="sr-only">Open menu</span>
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-64">
<SheetTitle className="px-4 pt-4">Acme</SheetTitle>
<nav className="mt-4 flex flex-col gap-1 px-2">
{links.map((link) => (
<a
key={link.label}
href={link.href}
className="hover:bg-accent rounded-md px-3 py-2 text-sm font-medium"
>
{link.label}
</a>
))}
<Button className="mt-2">Sign up</Button>
</nav>
</SheetContent>
</Sheet>
</div>
</header>
</div>
)
}
Mega menu
A wide, multi-column mega-menu dropdown — grouped link sections under one trigger.
Packages
Props
No props documented yet.
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from "@/components/ui/navigation-menu"
const sections = [
{
heading: "Marketing",
items: ["Landing pages", "SEO tools", "Email campaigns", "Analytics"],
},
{
heading: "Sales",
items: ["CRM", "Pipeline", "Forecasting", "Quotes"],
},
{
heading: "Support",
items: ["Help desk", "Live chat", "Knowledge base", "Community"],
},
]
export default function NavigationMenuMega01() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Solutions</NavigationMenuTrigger>
<NavigationMenuContent>
<div className="grid w-[600px] grid-cols-3 gap-4 p-4">
{sections.map((section) => (
<div key={section.heading}>
<p className="text-muted-foreground mb-2 px-2 text-xs font-medium uppercase">
{section.heading}
</p>
<ul className="space-y-1">
{section.items.map((item) => (
<li key={item}>
<NavigationMenuLink asChild>
<a
href="#"
className="hover:bg-accent block rounded-md px-2 py-1.5 text-sm"
>
{item}
</a>
</NavigationMenuLink>
</li>
))}
</ul>
</div>
))}
</div>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)
}
Active link
Highlighting the current route — the `active` prop sets `data-active` on the matching link.
Packages
Props
No props documented yet.
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"
const links = [
{ label: "Dashboard", href: "#" },
{ label: "Projects", href: "#" },
{ label: "Team", href: "#" },
{ label: "Settings", href: "#" },
]
// In a real app this comes from the router (usePathname).
const activeLabel = "Dashboard"
export default function NavigationMenuActive01() {
return (
<NavigationMenu>
<NavigationMenuList>
{links.map((link) => (
<NavigationMenuItem key={link.label}>
<NavigationMenuLink
asChild
active={link.label === activeLabel}
className={navigationMenuTriggerStyle()}
>
<a href={link.href}>{link.label}</a>
</NavigationMenuLink>
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
)
}
The Shadcn Navbar is built on the Navigation Menu — a Radix-based component for site navigation with links and animated dropdowns. Compose it into a flat link bar, a feature dropdown, a mega menu, or a complete responsive navbar with a mobile hamburger. The examples below cover all of those.
Anatomy
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
<NavigationMenuContent>{/* dropdown grid */}</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink className={navigationMenuTriggerStyle()} href="#">
Docs
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
A NavigationMenuItem is either a trigger + content (a dropdown) or a plain link. Hover or focus opens the dropdowns, which animate inside a shared viewport.
Building a navbar
A real navbar is a header row with three parts — logo, nav, actions:
<header className="flex h-14 items-center justify-between border-b px-4">
<Logo />
<NavigationMenu className="hidden md:flex">{/* links */}</NavigationMenu>
<Button size="sm">Sign up</Button>
</header>
The Responsive navbar example is the complete version.
Responsive: the mobile menu
The Navigation Menu is hover-driven, so it's a desktop pattern. On small screens, hide it and show a hamburger that opens a Sheet with the links stacked:
<NavigationMenu className="hidden md:flex">…</NavigationMenu>
<Sheet>
<SheetTrigger asChild>
<Button variant="outline" size="icon" className="md:hidden"><Menu /></Button>
</SheetTrigger>
<SheetContent side="right">{/* stacked links */}</SheetContent>
</Sheet>
Dropdowns & mega menus
- Feature dropdown — a grid of links with icons, titles, and descriptions (the Feature dropdown example).
- Mega menu — a wide
NavigationMenuContentwith a multi-column grid of grouped sections (the Mega menu example).
Active link
Pass active to the link for the current route — it sets data-active="true" and the styles highlight it. Derive it from your router:
<NavigationMenuLink active={pathname === href} href={href}>…</NavigationMenuLink>
Navigation Menu vs Dropdown Menu vs Menubar
- Navigation Menu (this) — site navigation, a navbar with hover dropdowns of links.
- Dropdown Menu — a click-triggered menu of actions (a row's "…" menu).
- Menubar — an application menu bar (File, Edit, View).
Accessibility
Radix manages roles, keyboard navigation (arrow keys move between items, Escape closes), and focus. Dropdowns open on hover and focus so they're keyboard-reachable, and the active link carries data-active for styling. Provide a visible label on the mobile hamburger (an sr-only "Open menu" works).