Aura Design System

Menubar

A visually persistent menu common in desktop applications.

Preview

Loading...
import {
  Menubar,
  MenubarMenu,
  MenubarTrigger,
  MenubarContent,
  MenubarItem,
  MenubarSeparator,
  MenubarCheckboxItem,
  MenubarRadioGroup,
  MenubarRadioItem,
  MenubarSubContent,
  MenubarSubTrigger,
  MenubarSub,
  MenubarShortcut,
} from "@/components/ui/Menubar";

export function MenubarDemo() {
  return (
  <Menubar>
    <MenubarMenu>
      <MenubarTrigger>File</MenubarTrigger>
      <MenubarContent className="w-20">
        <MenubarItem>
          New Tab <MenubarShortcut>⌘T</MenubarShortcut>
        </MenubarItem>
        <MenubarItem>
          New Window <MenubarShortcut>⌘N</MenubarShortcut>
        </MenubarItem>
        <MenubarItem disabled>New Incognito Window</MenubarItem>
        <MenubarSeparator />
        <MenubarSub>
          <MenubarSubTrigger>Share</MenubarSubTrigger>
          <MenubarSubContent className="w-20">
            <MenubarItem>Email link</MenubarItem>
            <MenubarItem>Messages</MenubarItem>
            <MenubarItem>Notes</MenubarItem>
          </MenubarSubContent>
        </MenubarSub>
        <MenubarSeparator />
        <MenubarItem>
          Print... <MenubarShortcut>⌘P</MenubarShortcut>
        </MenubarItem>
      </MenubarContent>
    </MenubarMenu>
    <MenubarMenu>
      <MenubarTrigger>Edit</MenubarTrigger>
      <MenubarContent className="w-20">
        <MenubarItem>
          Undo <MenubarShortcut>⌘Z</MenubarShortcut>
        </MenubarItem>
        <MenubarItem>
          Redo <MenubarShortcut>⇧⌘Z</MenubarShortcut>
        </MenubarItem>
        <MenubarSeparator />
        <MenubarSub>
          <MenubarSubTrigger>Find</MenubarSubTrigger>
          <MenubarSubContent className="w-20">
            <MenubarItem>Search the web</MenubarItem>
            <MenubarSeparator />
            <MenubarItem>Find...</MenubarItem>
            <MenubarItem>Find Next</MenubarItem>
            <MenubarItem>Find Previous</MenubarItem>
          </MenubarSubContent>
        </MenubarSub>
        <MenubarSeparator />
        <MenubarItem>Cut</MenubarItem>
        <MenubarItem>Copy</MenubarItem>
        <MenubarItem>Paste</MenubarItem>
      </MenubarContent>
    </MenubarMenu>
    <MenubarMenu>
      <MenubarTrigger>View</MenubarTrigger>
      <MenubarContent className="w-20">
        <MenubarCheckboxItem>Always Show Bookmarks Bar</MenubarCheckboxItem>
        <MenubarCheckboxItem checked>Always Show Full URLs</MenubarCheckboxItem>
        <MenubarSeparator />
        <MenubarItem>
          Reload <MenubarShortcut>⌘R</MenubarShortcut>
        </MenubarItem>
        <MenubarItem disabled>
          Force Reload <MenubarShortcut>⇧⌘R</MenubarShortcut>
        </MenubarItem>
        <MenubarSeparator />
        <MenubarItem>Toggle Fullscreen</MenubarItem>
        <MenubarSeparator />
        <MenubarItem>Hide Sidebar</MenubarItem>
      </MenubarContent>
    </MenubarMenu>
    <MenubarMenu>
      <MenubarTrigger>Profiles</MenubarTrigger>
      <MenubarContent className="w-20">
        <MenubarRadioGroup value="benoit">
          <MenubarRadioItem value="andy">Andy</MenubarRadioItem>
          <MenubarRadioItem value="benoit">Benoit</MenubarRadioItem>
          <MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
        </MenubarRadioGroup>
        <MenubarSeparator />
        <MenubarItem>Edit...</MenubarItem>
        <MenubarSeparator />
        <MenubarItem>Add Profile...</MenubarItem>
      </MenubarContent>
    </MenubarMenu>
  </Menubar>
)
}

Installation

Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces

pnpm dlx shadcn@latest add @aura/menubar

Manual

Install the following dependencies:

pnpm install @radix-ui/react-icons radix-ui

Copy and paste the class names utility into your utils/class-names.ts file.

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Copy and paste the Menubar component into your components/ui/Menubar.tsx file.

"use client";
/**
 * @description A visually persistent menu common in desktop applications.
 */
import * as React from "react";
import { Menubar as MenubarRadix } from "radix-ui";
import {
  CheckIcon,
  ChevronRightIcon,
  DotFilledIcon,
} from "@radix-ui/react-icons";

import { cn } from "@/utils/class-names";

function Menubar({
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.Root>) {
  return (
    <MenubarRadix.Root
      data-slot="menubar"
      className={cn("flex", className)}
      {...props}
    />
  );
}

function MenubarMenu({
  ...props
}: React.ComponentProps<typeof MenubarRadix.Menu>) {
  return <MenubarRadix.Menu data-slot="menubar-menu" {...props} />;
}

function MenubarGroup({
  ...props
}: React.ComponentProps<typeof MenubarRadix.Group>) {
  return <MenubarRadix.Group data-slot="menubar-group" {...props} />;
}

function MenubarPortal({
  ...props
}: React.ComponentProps<typeof MenubarRadix.Portal>) {
  return <MenubarRadix.Portal data-slot="menubar-portal" {...props} />;
}

function MenubarRadioGroup({
  ...props
}: React.ComponentProps<typeof MenubarRadix.RadioGroup>) {
  return <MenubarRadix.RadioGroup data-slot="menubar-radio-group" {...props} />;
}

function MenubarTrigger({
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.Trigger>) {
  return (
    <MenubarRadix.Trigger
      data-slot="menubar-trigger"
      className={cn(
        "p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer",
        className
      )}
      {...props}
    />
  );
}

function MenubarContent({
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.Content>) {
  return (
    <MenubarPortal>
      <MenubarRadix.Content
        data-slot="menubar-content"
        className={cn(
          "bg-gray-1 border border-gray-a6 rounded-sm relative shadow-md",
          className
        )}
        {...props}
      />
    </MenubarPortal>
  );
}

function MenubarItem({
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.Item>) {
  return (
    <MenubarRadix.Item
      data-slot="menubar-item"
      className={cn(
        "p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer justify-between",
        className
      )}
      {...props}
    />
  );
}

function MenubarCheckboxItem({
  children,
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.CheckboxItem>) {
  return (
    <MenubarRadix.CheckboxItem
      data-slot="menubar-checkbox-item"
      className={cn(
        "p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer",
        className
      )}
      {...props}
    >
      <span className="absolute left-0.5 top-0 bottom-0 items-center flex justify-center">
        <MenubarRadix.ItemIndicator>
          <CheckIcon className="text-accent-9" />
        </MenubarRadix.ItemIndicator>
      </span>
      {children}
    </MenubarRadix.CheckboxItem>
  );
}

function MenubarRadioItem({
  children,
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.RadioItem>) {
  return (
    <MenubarRadix.RadioItem
      data-slot="menubar-radio-item"
      className={cn(
        "p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer",
        className
      )}
      {...props}
    >
      <span className="absolute left-0.5 top-0 bottom-0 items-center flex justify-center">
        <MenubarRadix.ItemIndicator>
          <DotFilledIcon className="fill-current text-accent-9" />
        </MenubarRadix.ItemIndicator>
      </span>
      {children}
    </MenubarRadix.RadioItem>
  );
}

function MenubarLabel({
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.Label>) {
  return (
    <MenubarRadix.Label
      data-slot="menubar-label"
      className={cn("p-0.5 px-2 text-gray-12", className)}
      {...props}
    />
  );
}

function MenubarSeparator({
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.Separator>) {
  return (
    <MenubarRadix.Separator
      data-slot="menubar-separator"
      className={cn("m-0.6 h-px bg-gray-a6", className)}
      {...props}
    />
  );
}

function MenubarShortcut({ ...props }: React.ComponentProps<"span">) {
  return <span data-slot="menubar-shortcut" {...props} />;
}

function MenubarSub({
  ...props
}: React.ComponentProps<typeof MenubarRadix.Sub>) {
  return <MenubarRadix.Sub data-slot="menubar-sub" {...props} />;
}

function MenubarSubTrigger({
  children,
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.SubTrigger> & {
  inset?: boolean;
}) {
  return (
    <MenubarRadix.SubTrigger
      data-slot="menubar-sub-trigger"
      className={cn(
        "p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer",
        className
      )}
      {...props}
    >
      {children}
      <div className="absolute right-0.5 top-0 bottom-0 items-center flex justify-center">
        <ChevronRightIcon />
      </div>
    </MenubarRadix.SubTrigger>
  );
}

function MenubarSubContent({
  className,
  ...props
}: React.ComponentProps<typeof MenubarRadix.SubContent>) {
  return (
    <MenubarRadix.SubContent
      data-slot="menubar-sub-content"
      className={cn(
        "bg-gray-1 border border-gray-a6 rounded-sm relative shadow-md",
        className
      )}
      {...props}
    />
  );
}

export {
  Menubar,
  MenubarPortal,
  MenubarMenu,
  MenubarTrigger,
  MenubarContent,
  MenubarGroup,
  MenubarSeparator,
  MenubarLabel,
  MenubarItem,
  MenubarShortcut,
  MenubarCheckboxItem,
  MenubarRadioGroup,
  MenubarRadioItem,
  MenubarSub,
  MenubarSubTrigger,
  MenubarSubContent,
};