UIComponentsNavigation MenuNew

Navigation Menu

A collection of links for navigating websites.

Installation

CLI

npx shadcn@latest add "https://eo-n.vercel.app/r/navigation-menu"

Manual

Copy and paste the following code into your project.

"use client";
 
import * as React from "react";
import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui-components/react/navigation-menu";
import { cva } from "class-variance-authority";
import { ChevronDownIcon } from "lucide-react";
 
import { cn } from "@/lib/utils";
 
interface NavigationMenuProps
  extends React.ComponentProps<typeof NavigationMenuPrimitive.Root> {
  contentProps?: Omit<
    React.ComponentProps<typeof NavigationMenuPrimitive.Positioner>,
    "render"
  >;
}
 
function NavigationMenu({
  className,
  children,
  contentProps,
  ...props
}: NavigationMenuProps) {
  return (
    <NavigationMenuPrimitive.Root
      data-slot="navigation-menu"
      className={cn(
        "group/navigation-menu relative flex max-w-max items-center justify-center",
        className
      )}
      {...props}
    >
      {children}
      <NavigationMenuViewport contentProps={contentProps} />
    </NavigationMenuPrimitive.Root>
  );
}
 
function NavigationMenuList({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
  return (
    <NavigationMenuPrimitive.List
      data-slot="navigation-menu-list"
      className={cn(
        "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-[popup-open]:hover:bg-accent data-[popup-open]:text-accent-foreground data-[popup-open]:focus:bg-accent data-[popup-open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 [&_svg]:top-[1px] [&_svg]:ml-1 [&[data-popup-open]_svg]:rotate-180"
);
 
function NavigationMenuTrigger({
  className,
  children,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
  return (
    <NavigationMenuPrimitive.Trigger
      data-slot="navigation-menu-trigger"
      className={cn(navigationMenuTriggerStyle(), className)}
      {...props}
    >
      {children}
      <NavigationMenuIcon />
    </NavigationMenuPrimitive.Trigger>
  );
}
 
function NavigationMenuIcon({
  className,
  render = <ChevronDownIcon />,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Icon>) {
  return (
    <NavigationMenuPrimitive.Icon
      className={cn(
        "pointer-events-none relative size-3.5 shrink-0 transition duration-300",
        className
      )}
      render={render}
      {...props}
    />
  );
}
 
function NavigationMenuContent({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
  return (
    <NavigationMenuPrimitive.Content
      data-slot="navigation-menu-content"
      className={cn(
        "size-auto p-4 transition-[opacity,transform,translate] duration-300 ease-out",
        "data-[ending-style]:opacity-0 data-[ending-style]:data-[activation-direction=left]:translate-x-[50%] data-[ending-style]:data-[activation-direction=right]:translate-x-[-50%] data-[starting-style]:opacity-0 data-[starting-style]:data-[activation-direction=left]:translate-x-[-50%] data-[starting-style]:data-[activation-direction=right]:translate-x-[50%]",
        "**:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
        className
      )}
      {...props}
    />
  );
}
 
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 no-underline transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    />
  );
}
 
function NavigationMenuBackdrop({
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Backdrop>) {
  return (
    <NavigationMenuPrimitive.Backdrop
      data-slot="navigation-menu-backdrop"
      {...props}
    />
  );
}
 
function NavigationMenuPortal({
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Portal>) {
  return (
    <NavigationMenuPrimitive.Portal
      data-slot="navigation-menu-portal"
      {...props}
    />
  );
}
 
interface NavigationMenuViewportProps
  extends React.ComponentProps<typeof NavigationMenuPrimitive.Viewport> {
  contentProps?: Omit<
    React.ComponentProps<typeof NavigationMenuPrimitive.Positioner>,
    "render"
  >;
}
 
function NavigationMenuViewport({
  className: viewportClassName,
  children,
  contentProps,
  ...props
}: NavigationMenuViewportProps) {
  const {
    sideOffset = 6,
    collisionPadding = { top: 5, bottom: 5, left: 20, right: 20 },
    className,
    ...rest
  } = contentProps ?? {};
 
  return (
    <NavigationMenuPortal>
      <NavigationMenuBackdrop />
      <NavigationMenuPrimitive.Positioner
        data-slot="navigation-menu-positioner"
        sideOffset={sideOffset}
        collisionPadding={collisionPadding}
        className={cn(
          "z-50 box-border h-[var(--positioner-height)] w-[var(--positioner-width)] duration-300 ease-out",
          className
        )}
        {...rest}
      >
        <NavigationMenuPrimitive.Popup
          data-slot="navigation-menu-content"
          className={cn(
            "bg-popover relative h-[var(--popup-height)] w-full rounded-md border shadow transition-all duration-150 ease-out min-[500px]:w-[var(--popup-width)] md:w-[var(--popup-width)]",
            "origin-[var(--transform-origin)] data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
            className
          )}
        >
          <NavigationMenuPrimitive.Viewport
            data-slot="navigation-menu-viewport"
            className={cn(
              "relative h-full w-full overflow-hidden",
              viewportClassName
            )}
            {...props}
          >
            {children}
          </NavigationMenuPrimitive.Viewport>
        </NavigationMenuPrimitive.Popup>
      </NavigationMenuPrimitive.Positioner>
    </NavigationMenuPortal>
  );
}
 
function NavigationSubMenuTrigger({
  className,
  children,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
  return (
    <NavigationMenuPrimitive.Trigger
      data-slot="navigation-menu-sub-trigger"
      className={cn(className)}
      {...props}
    >
      {children}
    </NavigationMenuPrimitive.Trigger>
  );
}
 
export {
  NavigationMenu,
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuTrigger,
  NavigationMenuIcon,
  NavigationMenuContent,
  NavigationMenuLink,
  NavigationMenuBackdrop,
  NavigationMenuPortal,
  NavigationMenuViewport,
  navigationMenuTriggerStyle,
  NavigationSubMenuTrigger,
};
Update the import paths to match your project setup.

Usage

Import all parts and piece them together.

import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Menubar } from "@/components/ui/menubar";
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Item One</NavigationMenuTrigger>
      <NavigationMenuContent>
        <NavigationMenuLink>Link</NavigationMenuLink>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>

Examples

Nested Sub Menu

On this page