Menubar
Menus & Navigationimport { Menubar } from "bits-ui" Application menubar
File, Edit, and View menus with shortcuts, a submenu, and persistent view toggles
<script lang="ts">
import { Menubar } from 'bits-ui';
import { Check, ChevronRight } from '@lucide/svelte';
let showSidebar = $state(true);
let showStatus = $state(true);
const itemClass =
'flex min-w-52 cursor-default select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm outline-none data-highlighted:bg-ink-800 data-highlighted:text-ink-50 data-disabled:opacity-40';
const triggerClass =
'cursor-default rounded-md px-3 py-1.5 text-sm font-medium outline-none data-highlighted:bg-ink-800 data-[state=open]:bg-ink-800';
</script>
<Menubar.Root class="card mx-auto flex w-fit items-center gap-1 p-1">
<Menubar.Menu value="file">
<Menubar.Trigger class={triggerClass}>File</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content class="panel animate-pop-in p-1" align="start" sideOffset={5}>
<Menubar.Item class={itemClass}>New workspace <kbd class="ml-auto font-mono text-[0.68rem] text-ink-400">⌘ N</kbd></Menubar.Item>
<Menubar.Item class={itemClass}>Open… <kbd class="ml-auto font-mono text-[0.68rem] text-ink-400">⌘ O</kbd></Menubar.Item>
<Menubar.Separator class="my-1 h-px bg-ink-800" />
<Menubar.Sub>
<Menubar.SubTrigger class={itemClass}>Open recent <ChevronRight class="ml-auto size-4 text-ink-400" /></Menubar.SubTrigger>
<Menubar.SubContent class="panel animate-pop-in p-1" sideOffset={8}>
<Menubar.Item class={itemClass}>Atlas redesign</Menubar.Item>
<Menubar.Item class={itemClass}>Observability console</Menubar.Item>
<Menubar.Item class={itemClass}>Release dashboard</Menubar.Item>
</Menubar.SubContent>
</Menubar.Sub>
<Menubar.Separator class="my-1 h-px bg-ink-800" />
<Menubar.Item class={itemClass}>Export snapshot</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu value="edit">
<Menubar.Trigger class={triggerClass}>Edit</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content class="panel animate-pop-in p-1" align="start" sideOffset={5}>
<Menubar.Item class={itemClass}>Undo <kbd class="ml-auto font-mono text-[0.68rem] text-ink-400">⌘ Z</kbd></Menubar.Item>
<Menubar.Item class={itemClass}>Redo <kbd class="ml-auto font-mono text-[0.68rem] text-ink-400">⇧ ⌘ Z</kbd></Menubar.Item>
<Menubar.Separator class="my-1 h-px bg-ink-800" />
<Menubar.Item class={itemClass}>Cut <kbd class="ml-auto font-mono text-[0.68rem] text-ink-400">⌘ X</kbd></Menubar.Item>
<Menubar.Item class={itemClass}>Copy <kbd class="ml-auto font-mono text-[0.68rem] text-ink-400">⌘ C</kbd></Menubar.Item>
<Menubar.Item class={itemClass}>Paste <kbd class="ml-auto font-mono text-[0.68rem] text-ink-400">⌘ V</kbd></Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu value="view">
<Menubar.Trigger class={triggerClass}>View</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content class="panel animate-pop-in p-1" align="start" sideOffset={5}>
<Menubar.CheckboxItem bind:checked={showSidebar} class={itemClass}>
{#snippet children({ checked })}
Show sidebar
{#if checked}<Check class="ml-auto size-4 text-brand-400" />{/if}
{/snippet}
</Menubar.CheckboxItem>
<Menubar.CheckboxItem bind:checked={showStatus} class={itemClass}>
{#snippet children({ checked })}
Show status bar
{#if checked}<Check class="ml-auto size-4 text-brand-400" />{/if}
{/snippet}
</Menubar.CheckboxItem>
<Menubar.Separator class="my-1 h-px bg-ink-800" />
<Menubar.Item class={itemClass}>Enter full screen</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
Controlled active menu
Root value identifies the open Menu and can be driven from elsewhere in the interface
Active value: none
<script lang="ts">
import { Menubar } from 'bits-ui';
let value = $state('');
const itemClass =
'flex min-w-48 cursor-default select-none items-center rounded-md px-2.5 py-2 text-sm outline-none data-highlighted:bg-ink-800 data-highlighted:text-ink-50';
const triggerClass =
'cursor-default rounded-md px-3 py-1.5 text-sm font-medium outline-none data-highlighted:bg-ink-800 data-[state=open]:bg-ink-800';
</script>
<div class="grid justify-items-center gap-3">
<div class="flex flex-wrap justify-center gap-2">
<button class="btn btn-sm" onclick={() => (value = 'project')}>Open Project</button>
<button class="btn btn-sm" onclick={() => (value = 'share')}>Open Share</button>
<button class="btn btn-sm" onclick={() => (value = '')}>Close</button>
</div>
<Menubar.Root bind:value class="card flex w-fit items-center gap-1 p-1">
<Menubar.Menu value="project">
<Menubar.Trigger class={triggerClass}>Project</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content class="panel animate-pop-in p-1" align="start" sideOffset={5}>
<Menubar.Item class={itemClass}>Project settings</Menubar.Item>
<Menubar.Item class={itemClass}>Manage environments</Menubar.Item>
<Menubar.Item class={itemClass}>Archive project</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu value="share">
<Menubar.Trigger class={triggerClass}>Share</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content class="panel animate-pop-in p-1" align="start" sideOffset={5}>
<Menubar.Item class={itemClass}>Copy public link</Menubar.Item>
<Menubar.Item class={itemClass}>Invite collaborator</Menubar.Item>
<Menubar.Item class={itemClass}>Review access</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
<p class="muted text-xs">Active value: <span class="font-mono text-ink-200">{value || 'none'}</span></p>
</div>
Left and Right move between menu triggers; arrow keys move within an open menu, Enter or Space selects, and Escape returns focus and closes it. Root's loop defaults to true for trigger navigation, while Content also exposes a loop option for its items. Use item textValue to make icon-heavy labels participate in typeahead.
Give each Menu a stable value when binding Root's active value. Content is portalled to the body by default, and GroupHeading must be nested in a Group or RadioGroup; standalone headings throw during development and are skipped by keyboard navigation.