MC Navigation Menu

A navigation menu component for site-wide navigation.

Overview

The mc-navigation-menu is a flexible navigation system for top-level site structure and grouped links. It is built on the @base-ui/react navigation menu primitives, then styled with Tailwind CSS v4 and utility variants from class-variance-authority.

As a part of the MicroClub library, this component is designed for instant deployment into your codebase, giving you full ownership of its look and behavior.

Preview

Loading...
'use client';import * as React from 'react';import Link from 'next/link';import { CircleAlertIcon, CircleCheckIcon, CircleDashedIcon } from 'lucide-react';import {  McNavigationMenu,  McNavigationMenuContent,  McNavigationMenuItem,  McNavigationMenuLink,  McNavigationMenuList,  McNavigationMenuTrigger,  navigationMenuTriggerStyle,} from '../ui/mc-navigation-menu';const components: { title: string; href: string; description: string }[] = [  {    title: 'Alert Dialog',    href: '#alert-dialog',    description:      'A modal dialog that interrupts the user with important content and expects a response.',  },  {    title: 'Hover Card',    href: '#hover-card',    description: 'For sighted users to preview content available behind a link.',  },  {    title: 'Progress',    href: '#progress',    description:      'Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.',  },  {    title: 'Scroll-area',    href: '#scroll-area',    description: 'Visually or semantically separates content.',  },  {    title: 'Tabs',    href: '#tabs',    description:      'A set of layered sections of content—known as tab panels—that are displayed one at a time.',  },  {    title: 'Tooltip',    href: '#tooltip',    description:      'A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.',  },];export default function NavigationMenuDemo() {  return (    <McNavigationMenu>      <McNavigationMenuList>        <McNavigationMenuItem>          <McNavigationMenuTrigger>Getting started</McNavigationMenuTrigger>          <McNavigationMenuContent>            <ul className="w-96">              <ListItem href="#docs" title="Introduction">                Re-usable components built with Tailwind CSS.              </ListItem>              <ListItem href="#installation" title="Installation">                How to install dependencies and structure your app.              </ListItem>              <ListItem href="#typography" title="Typography">                Styles for headings, paragraphs, lists...etc              </ListItem>            </ul>          </McNavigationMenuContent>        </McNavigationMenuItem>        <McNavigationMenuItem className="hidden md:flex">          <McNavigationMenuTrigger>Components</McNavigationMenuTrigger>          <McNavigationMenuContent>            <ul className="grid w-[400px] gap-2 md:w-[500px] md:grid-cols-2 lg:w-[600px]">              {components.map((component) => (                <ListItem key={component.title} title={component.title} href={component.href}>                  {component.description}                </ListItem>              ))}            </ul>          </McNavigationMenuContent>        </McNavigationMenuItem>        <McNavigationMenuItem>          <McNavigationMenuTrigger>With Icon</McNavigationMenuTrigger>          <McNavigationMenuContent>            <ul className="grid w-[200px]">              <li>                <McNavigationMenuLink                  render={                    <Link href="#" className="flex items-center gap-2">                      <CircleAlertIcon />                      Backlog                    </Link>                  }                />                <McNavigationMenuLink                  render={                    <Link href="#" className="flex items-center gap-2">                      <CircleDashedIcon />                      To Do                    </Link>                  }                />                <McNavigationMenuLink                  render={                    <Link href="#" className="flex items-center gap-2">                      <CircleCheckIcon />                      Done                    </Link>                  }                />              </li>            </ul>          </McNavigationMenuContent>        </McNavigationMenuItem>        <McNavigationMenuItem>          <McNavigationMenuLink            className={navigationMenuTriggerStyle()}            render={<Link href="#docs">Docs</Link>}          />        </McNavigationMenuItem>      </McNavigationMenuList>    </McNavigationMenu>  );}function ListItem({  title,  children,  href,  ...props}: React.ComponentPropsWithoutRef<'li'> & { href: string }) {  return (    <li {...props}>      <McNavigationMenuLink        render={          <Link href={href}>            <div className="flex flex-col gap-1 text-sm">              <div className="leading-none font-medium">{title}</div>              <div className="line-clamp-2 text-muted-foreground">{children}</div>            </div>          </Link>        }      />    </li>  );}

Add to Your Project

Deploy the mc-navigation-menu directly to your components/ui directory:

npx mcoli-ui@latest add mc-navigation-menu
pnpm dlx mcoli-ui@latest add mc-navigation-menu
yarn dlx mcoli-ui@latest add mc-navigation-menu
bunx mcoli-ui@latest add mc-navigation-menu

Manual Setup

If you prefer manual control:

Install the core dependencies
npm install @base-ui/react lucide-react
pnpm add @base-ui/react lucide-react
yarn add @base-ui/react lucide-react
bun add @base-ui/react lucide-react
Copy the source code
import { NavigationMenu as NavigationMenuPrimitive } from '@base-ui/react/navigation-menu';import { cva } from 'class-variance-authority';import { ChevronDownIcon } from 'lucide-react';import { cn } from '@/lib/utils';function McNavigationMenu({  align = 'start',  className,  children,  ...props}: NavigationMenuPrimitive.Root.Props & Pick<NavigationMenuPrimitive.Positioner.Props, 'align'>) {  return (    <NavigationMenuPrimitive.Root      data-slot="navigation-menu"      className={cn(        'group/navigation-menu relative flex max-w-max flex-1 items-center justify-center',        className      )}      {...props}    >      {children}      <McNavigationMenuPositioner align={align} />    </NavigationMenuPrimitive.Root>  );}function McNavigationMenuList({  className,  ...props}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.List>) {  return (    <NavigationMenuPrimitive.List      data-slot="navigation-menu-list"      className={cn(        'group flex flex-1 list-none items-center justify-evenly gap-1 ring-1 ring-inset ring-border bg-background rounded-md ',        className      )}      {...props}    />  );}function McNavigationMenuItem({  className,  ...props}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item>) {  return (    <NavigationMenuPrimitive.Item      data-slot="navigation-menu-item"      className={cn('relative', className)}      {...props}    />  );}const navigationMenuTriggerStyle = cva(  'group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center paragraph-sm font-medium transition-all outline-none focus-visible:bg-accent focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-accent data-popup-open:hover:bg-accent data-open:bg-accent/50 data-open:hover:bg-accent data-open:focus:bg-accent data-popup-open:shadow-[inset_0_0_0_2px_var(--border),0_0_0_2px_var(--border)] data-open:shadow-[inset_0_0_0_2px_var(--border),0_0_0_2px_var(--border)] data-popup-open:hover:shadow-[inset_0_0_0_2px_var(--border),0_0_0_2px_var(--border)] data-popup-open:hover:bg-accent px-4 py-2 rounded-md');function McNavigationMenuTrigger({  className,  children,  ...props}: NavigationMenuPrimitive.Trigger.Props) {  return (    <NavigationMenuPrimitive.Trigger      data-slot="navigation-menu-trigger"      className={cn(        navigationMenuTriggerStyle(),        'group',        className,        'text-foreground  data-popup-open:text-accent-foreground paragraph-sm text-medium'      )}      {...props}    >      {children}{' '}      <ChevronDownIcon        className="relative top-px ml-1 size-3 transition duration-300 group-data-popup-open/navigation-menu-trigger:rotate-180 group-data-open/navigation-menu-trigger:rotate-180"        aria-hidden="true"      />    </NavigationMenuPrimitive.Trigger>  );}function McNavigationMenuContent({ className, ...props }: NavigationMenuPrimitive.Content.Props) {  return (    <NavigationMenuPrimitive.Content      data-slot="navigation-menu-content"      className={cn(        'data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] h-full w-auto p-4 transition-[opacity,transform,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 data-ending-style:opacity-0 data-starting-style:opacity-0 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 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95',        className      )}      {...props}    />  );}function McNavigationMenuPositioner({  className,  side = 'bottom',  sideOffset = 8,  align = 'start',  alignOffset = 0,  ...props}: NavigationMenuPrimitive.Positioner.Props) {  return (    <NavigationMenuPrimitive.Portal>      <NavigationMenuPrimitive.Positioner        side={side}        sideOffset={sideOffset}        align={align}        alignOffset={alignOffset}        className={cn(          'isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0',          className        )}        {...props}      >        <NavigationMenuPrimitive.Popup className="data-[ending-style]:easing-[ease] xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) rounded-lg bg-popover text-popover-foreground ring-1 ring-border transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0">          <NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />        </NavigationMenuPrimitive.Popup>      </NavigationMenuPrimitive.Positioner>    </NavigationMenuPrimitive.Portal>  );}function McNavigationMenuLink({ className, ...props }: NavigationMenuPrimitive.Link.Props) {  return (    <NavigationMenuPrimitive.Link      data-slot="navigation-menu-link"      className={cn(        "flex items-center gap-2 rounded-lg px-2 py-3 paragraph-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md data-active:bg-muted/50 data-active:hover:bg-muted data-active:focus:bg-muted [&_svg:not([class*='size-'])]:size-4",        className      )}      {...props}    />  );}function McNavigationMenuIndicator({  className,  ...props}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Icon>) {  return (    <NavigationMenuPrimitive.Icon      data-slot="navigation-menu-indicator"      className={cn(        'top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in',        className      )}      {...props}    >      <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />    </NavigationMenuPrimitive.Icon>  );}export {  McNavigationMenu,  McNavigationMenuContent,  McNavigationMenuIndicator,  McNavigationMenuItem,  McNavigationMenuLink,  McNavigationMenuList,  McNavigationMenuTrigger,  navigationMenuTriggerStyle,  McNavigationMenuPositioner,};
Adjust import paths

Ensure your @/lib/utils helper is available, and confirm next/link is used where you render links inside menu content.

Usage

import Link from 'next/link';
import {
  McNavigationMenu,
  McNavigationMenuContent,
  McNavigationMenuItem,
  McNavigationMenuLink,
  McNavigationMenuList,
  McNavigationMenuTrigger,
} from '@/components/ui/mc-navigation-menu';

export default function Example() {
  return (
    <McNavigationMenu>
      <McNavigationMenuList>
        <McNavigationMenuItem>
          <McNavigationMenuTrigger>Docs</McNavigationMenuTrigger>
          <McNavigationMenuContent>
            <ul className="w-80">
              <li>
                <McNavigationMenuLink
                  render={<Link href="/docs/introduction">Introduction</Link>}
                />
              </li>
              <li>
                <McNavigationMenuLink
                  render={<Link href="/docs/installation">Installation</Link>}
                />
              </li>
            </ul>
          </McNavigationMenuContent>
        </McNavigationMenuItem>
      </McNavigationMenuList>
    </McNavigationMenu>
  );
}

Examples

Basic Trigger and Content

<McNavigationMenu>
  <McNavigationMenuList>
    <McNavigationMenuItem>
      <McNavigationMenuTrigger>Getting started</McNavigationMenuTrigger>
      <McNavigationMenuContent>
        <ul className="w-96">
          <li>
            <McNavigationMenuLink render={<Link href="/docs">Introduction</Link>} />
          </li>
          <li>
            <McNavigationMenuLink render={<Link href="/docs/installation">Installation</Link>} />
          </li>
        </ul>
      </McNavigationMenuContent>
    </McNavigationMenuItem>
  </McNavigationMenuList>
</McNavigationMenu>

Grid Layout for Rich Menus

<McNavigationMenuContent>
  <ul className="grid w-[400px] gap-2 md:w-[500px] md:grid-cols-2 lg:w-[600px]">
    {components.map((item) => (
      <li key={item.title}>
        <McNavigationMenuLink render={<Link href={item.href}>{item.title}</Link>} />
      </li>
    ))}
  </ul>
</McNavigationMenuContent>
import { CircleAlertIcon, CircleCheckIcon, CircleDashedIcon } from 'lucide-react';

<McNavigationMenuContent>
  <ul className="grid w-[200px]">
    <li>
      <McNavigationMenuLink
        render={
          <Link href="#" className="flex-row items-center gap-2">
            <CircleAlertIcon />
            Backlog
          </Link>
        }
      />
      <McNavigationMenuLink
        render={
          <Link href="#" className="flex-row items-center gap-2">
            <CircleDashedIcon />
            To Do
          </Link>
        }
      />
      <McNavigationMenuLink
        render={
          <Link href="#" className="flex-row items-center gap-2">
            <CircleCheckIcon />
            Done
          </Link>
        }
      />
    </li>
  </ul>
</McNavigationMenuContent>;
import { navigationMenuTriggerStyle } from '@/components/ui/mc-navigation-menu';

<McNavigationMenuItem>
  <McNavigationMenuLink
    className={navigationMenuTriggerStyle()}
    render={<Link href="/docs">Docs</Link>}
  />
</McNavigationMenuItem>;

API Reference

Components and Utilities

ExportTypeDefaultDescription
McNavigationMenuRoot componentalign="start"Main container that renders the menu root and a default positioner.
McNavigationMenuListComponentundefinedWraps top-level menu items in a horizontal list container.
McNavigationMenuItemComponentundefinedRepresents one trigger/content pair or standalone navigation node.
McNavigationMenuTriggerComponentundefinedInteractive trigger that opens content and includes a rotating chevron icon.
McNavigationMenuContentComponentundefinedPopup content region with open/close and directional motion styles.
McNavigationMenuLinkComponentundefinedStyled link primitive that supports Base UI polymorphic render usage.
McNavigationMenuPositionerComponentside="bottom", sideOffset={8}, align="start", alignOffset={0}Controls popup placement and viewport sizing behavior.
McNavigationMenuIndicatorComponentundefinedOptional indicator icon for the active menu item.
navigationMenuTriggerStyleCVA utilityundefinedUtility function returning trigger class names for reuse on custom links.

All other compatible props from @base-ui/react/navigation-menu primitives are supported on each corresponding wrapper component.

On this page