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

1 Configure registry — once per project
{
  "registries": {
    "@designrevision": "https://registry.designrevision.com/r/{name}.json"
  }
}

Add to your existing components.json. Requires shadcn/ui v2.3+.

2 Install the component

Packages

@radix-ui/react-navigation-menu
class-variance-authority
utils

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.

Copied!
components/ui/navigation-menu.tsx
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

navigation-menu

Props

No props documented yet.

Copied!
components/ui/navigation-menu-demo-01.tsx
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

navigation-menu

Props

No props documented yet.

Copied!
components/ui/navigation-menu-simple-01.tsx
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

lucide-react
navigation-menu

Props

No props documented yet.

Copied!
components/ui/navigation-menu-icons-01.tsx
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

lucide-react
navigation-menu
button
sheet

Props

No props documented yet.

Copied!
components/ui/navigation-menu-navbar-01.tsx
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

navigation-menu

Props

No props documented yet.

Copied!
components/ui/navigation-menu-mega-01.tsx
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

navigation-menu

Props

No props documented yet.

Copied!
components/ui/navigation-menu-active-01.tsx
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>
  )
}

Frequently asked questions

It is the component you use to build a site navbar — a Radix-based menu of links and dropdowns. Compose NavigationMenu → NavigationMenuList → NavigationMenuItem, each holding a NavigationMenuTrigger + NavigationMenuContent (a dropdown) or a plain NavigationMenuLink. Hover or focus opens the dropdowns, animated in a shared viewport.
Put your logo, the NavigationMenu, and any actions (a Sign up button) in a header row. On desktop the NavigationMenu shows the links and dropdowns; on mobile, hide it and show a hamburger that opens a Sheet with the same links. The Responsive navbar example is a complete, copy-paste navbar.
The Navigation Menu is hover-driven and best on desktop, so on small screens hide it (hidden md:flex) and render a hamburger button that opens a Sheet (a slide-in drawer) holding the links stacked vertically. The Responsive navbar example wires this up with our Sheet component.
Inside a NavigationMenuItem, pair a NavigationMenuTrigger (the top-level label) with a NavigationMenuContent (the panel). Fill the content with a grid of NavigationMenuLink items — titles, descriptions, even icons. The Dropdown menus and Feature dropdown examples show both styles.
A mega menu is just a wide NavigationMenuContent — give it a fixed width and a multi-column grid of grouped link sections. The Mega menu example lays out three columns of categories under a single trigger.
Pass active to the NavigationMenuLink for the current route — it sets data-active="true", which the link styles pick up (a subtle background). In a real app, derive it from the router, e.g. active={pathname === href}. The Active link example shows it.
Navigation Menu is for site navigation — a navbar with hover dropdowns of links. Dropdown Menu is a click-triggered menu of actions (like a row's "…" menu). Menubar is an application menu bar (File, Edit, View). Use Navigation Menu for your header nav.