Menubar
NavigationAn application command bar with menus and checkable settings.
Responsive viewport — resize the window to explore. Direction and density apply only to this preview. Theme applies to the docs too.
"use client";
import {
Menubar,
MenubarMenu,
MenubarTrigger,
MenubarContent,
MenubarItem,
MenubarCheckboxItem,
MenubarSeparator,
} from "@/components/ui/menubar";
import { useState } from "react";
export default function MenubarExample() {
const [action, setAction] = useState("No action selected");
const [toolbar, setToolbar] = useState(true);
return (
<div className="space-y-4">
<Menubar aria-label="Document editor">
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem onSelect={() => setAction("New document created")}>
New document
</MenubarItem>
<MenubarItem
onSelect={() => setAction("Document saved in preview")}
>
Save
</MenubarItem>
<MenubarSeparator />
<MenubarItem disabled>Export to PDF</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarCheckboxItem
checked={toolbar}
onCheckedChange={(value) => setToolbar(value === true)}
>
Show toolbar
</MenubarCheckboxItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
<p role="status" className="text-xs text-muted-foreground">
{action} · Toolbar {toolbar ? "visible" : "hidden"}
</p>
</div>
);
}Usage
Use MenubarMenu, MenubarTrigger, and MenubarContent to group commands. Attach onSelect to items and checked/onCheckedChange to checkbox items. Command behavior belongs to the consuming application.
import {
Menubar,
MenubarMenu,
MenubarTrigger,
MenubarContent,
MenubarItem,
MenubarCheckboxItem,
MenubarSeparator,
} from "@/components/ui/menubar";Shipped as editable source in src/components/ui/menubar.tsx in both apps. Copy the implementation below when moving it to another project. Copy the complete example from Source code for all imports and state. Add "use client" when using hooks or event handlers. See installation and theming for project setup.
Component source
The complete implementation is included in both apps. It uses the React, Radix, Lucide, and shared-core dependencies already installed in Apex. Keep your project’s semantic theme tokens when reusing it.
src/components/ui/menubar.tsx
"use client";
import { useDocumentDirection } from "@/lib/use-document-direction";
import type { ComponentProps } from "react";
import { cn } from "@dashboardpack/core/lib/utils";
import { Menubar as Primitive } from "radix-ui";
import { Check } from "lucide-react";
export function Menubar({
className,
...props
}: ComponentProps<typeof Primitive.Root>) {
const dir = useDocumentDirection();
return (
<Primitive.Root
dir={dir}
className={cn(
"inline-flex flex-wrap items-center gap-1 rounded-lg border bg-background p-1",
className,
)}
{...props}
/>
);
}
export const MenubarMenu = Primitive.Menu;
export function MenubarTrigger({
className,
...props
}: ComponentProps<typeof Primitive.Trigger>) {
return (
<Primitive.Trigger
className={cn(
"rounded-md px-3 py-1.5 text-sm font-medium outline-none focus:bg-accent data-[state=open]:bg-accent",
className,
)}
{...props}
/>
);
}
export function MenubarContent({
className,
sideOffset = 6,
...props
}: ComponentProps<typeof Primitive.Content>) {
return (
<Primitive.Portal>
<Primitive.Content
sideOffset={sideOffset}
className={cn(
"z-50 min-w-44 rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg",
className,
)}
{...props}
/>
</Primitive.Portal>
);
}
export function MenubarItem({
className,
...props
}: ComponentProps<typeof Primitive.Item>) {
return (
<Primitive.Item
className={cn(
"flex cursor-default items-center gap-2 rounded-md px-2 py-2 text-sm outline-none focus:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
{...props}
/>
);
}
export function MenubarCheckboxItem({
className,
children,
...props
}: ComponentProps<typeof Primitive.CheckboxItem>) {
return (
<Primitive.CheckboxItem
className={cn(
"relative flex cursor-default items-center rounded-md py-2 pe-2 ps-8 text-sm outline-none focus:bg-accent",
className,
)}
{...props}
>
<span className="absolute start-2">
<Primitive.ItemIndicator>
<Check className="size-4" />
</Primitive.ItemIndicator>
</span>
{children}
</Primitive.CheckboxItem>
);
}
export function MenubarSeparator(
props: ComponentProps<typeof Primitive.Separator>,
) {
return <Primitive.Separator className="my-1 h-px bg-border" {...props} />;
}Required helper: src/lib/use-document-direction.ts
Copy this helper alongside the component. It keeps keyboard navigation aligned with the page direction and supports live RTL changes.
"use client";
import { useSyncExternalStore } from "react";
function subscribe(notify: () => void) {
const observer = new MutationObserver(notify);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["dir"],
});
return () => observer.disconnect();
}
function snapshot(): "ltr" | "rtl" {
return document.documentElement.dir === "rtl" ? "rtl" : "ltr";
}
/** Keep headless controls aligned with the document, including live RTL changes. */
export function useDocumentDirection() {
return useSyncExternalStore(subscribe, snapshot, () => "ltr" as const);
}API reference
Common props and defaults. Slot-specific props are named below; native elements also accept their standard HTML attributes. The component TypeScript definitions are the complete type reference.
| Prop | Type / Default | Description |
|---|---|---|
| value / defaultValue | stringDefault: — | Controlled or initial active menu. |
| onValueChange | (value: string) => voidDefault: — | Handle active-menu changes. |
| onSelect (MenubarItem) | (event: Event) => voidDefault: — | Invoke an application command. |
| checked / onCheckedChange | boolean | indeterminate / callbackDefault: — | Control a checkbox item. |
| sideOffset (MenubarContent) | numberDefault: 6 | Gap below the trigger. |