Menubar
Features
- Can be controlled or uncontrolled.
- Supports submenus with configurable reading direction.
- Supports items, labels, groups of items.
- Supports checkable items (single or multiple).
- Customize side, alignment, offsets, collision handling.
- Optionally render a pointing arrow.
- Focus is fully managed.
- Full keyboard navigation.
- Typeahead support.
Installation
Install the component from your command line.
npm install radix-vue
Anatomy
Import all parts and piece them together.
<script setup lang="ts">
import {
MenubarArrow,
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarLabel,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup,
MenubarRadioItem,
MenubarRoot,
MenubarSeparator,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from './'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger />
<MenubarPortal>
<MenubarContent>
<MenubarLabel />
<MenubarItem />
<MenubarGroup>
<MenubarItem />
</MenubarGroup>
<MenubarCheckboxItem>
<MenubarItemIndicator />
</MenubarCheckboxItem>
<MenubarRadioGroup>
<MenubarRadioItem>
<MenubarItemIndicator />
</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSub>
<MenubarSubTrigger />
<MenubarPortal>
<MenubarSubContent />
</MenubarPortal>
</MenubarSub>
<MenubarSeparator />
<MenubarArrow />
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
API Reference
Root
Contains all the parts of a menubar
Prop | Type | Default |
---|---|---|
defaultValue | string | |
modelValue | string | |
dir | enum | |
loop | boolean | false |
as | string | Component | div |
asChild | boolean | false |
Emit | Type |
---|---|
@update:modelValue | (value: string) => void |
Menu
A top level menu item, contains a trigger with content combination.
Prop | Type | Default |
---|---|---|
value | string |
Trigger
The button that toggles the content. By default, the MenubarContent
will position itself against the trigger.
Prop | Type | Default |
---|---|---|
as | string | Component | button |
asChild | boolean | false |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Portal
When used, portals the content part into the body
.
Prop | Type | Default |
---|---|---|
to | string | HTMLElement | body |
Content
The component that pops out when a menu is open.
Prop | Type | Default |
---|---|---|
loop | boolean | false |
forceMount | boolean | |
side | enum | "bottom" |
sideOffset | number | 0 |
align | enum | "center" |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
arrowPadding | number | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
as | string | Component | div |
asChild | boolean | false |
Emit | Type |
---|---|
@closeAutoFocus | (event: Event) => void |
@escapeKeyDown | (event: KeyboardEvent) => void |
@pointerDownOutside | (event: PointerDownOutsideEvent) => void |
@focusOutside | (event: FocusOutsideEvent) => void |
@interactOutside | (event: FocusEvent | MouseEvent | TouchEvent) => void |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-menubar-content-transform-origin |
The transform-origin computed from the content and arrow positions/offsets
|
--radix-menubar-content-available-width |
The remaining width between the trigger and the boundary edge
|
--radix-menubar-content-available-height |
The remaining height between the trigger and the boundary edge
|
--radix-menubar-trigger-width | The width of the trigger |
--radix-menubar-trigger-height | The height of the trigger |
Arrow
An optional arrow element to render alongside a menubar menu. This can be used to help visually link the trigger with the MenubarContent
. Must be rendered inside MenubarContent
.
Prop | Type | Default |
---|---|---|
as | string | Component | svg |
asChild | boolean | false |
width | number | 10 |
height | number | 5 |
Item
The component that contains the menubar items.
Prop | Type | Default |
---|---|---|
disabled | boolean | |
textValue | string | |
as | string | Component | div |
asChild | boolean | false |
Emit | Type |
---|---|
@select | (event: Event) => void |
Data Attribute | Value |
---|---|
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Group
Used to group multiple MenubarItem
s.
Prop | Type | Default |
---|---|---|
as | string | Component | div |
asChild | boolean | false |
Label
Used to render a label. It won't be focusable using arrow keys.
Prop | Type | Default |
---|---|---|
as | string | Component | label |
asChild | boolean | false |
CheckboxItem
An item that can be controlled and rendered like a checkbox.
Prop | Type | Default |
---|---|---|
checked | boolean | 'indeterminate' | |
disabled | boolean | |
textValue | string | |
as | string | Component | div |
asChild | boolean | false |
Emit | Type |
---|---|
@update:checked | (checked: boolean) => void |
@select | (event: Event) => void |
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
RadioGroup
Used to group multiple MenubarRadioItem
s.
Prop | Type | Default |
---|---|---|
as | string | Component | div |
asChild | boolean | false |
modelValue | string |
Emit | Type |
---|---|
@update:modelValue | (value: string) => void |
RadioItem
An item that can be controlled and rendered like a radio.
Prop | Type | Default |
---|---|---|
as | string | Component | div |
asChild | boolean | false |
value* | string | |
disabled | boolean | |
textValue | string |
Emit | Type |
---|---|
@select | (event: Event) => void |
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
ItemIndicator
Renders when the parent MenubarCheckboxItem
or MenubarRadioItem
is checked. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.
Prop | Type | Default |
---|---|---|
as | string | Component | span |
asChild | boolean | false |
forceMount | boolean |
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" |
Separator
Used to visually separate items in a menubar menu.
Prop | Type | Default |
---|---|---|
as | string | Component | div |
asChild | boolean | false |
Sub
Contains all the parts of a submenu.
Prop | Type | Default |
---|---|---|
defaultOpen | boolean | |
open | boolean |
Emit | Type |
---|---|
@update:open | (open: boolean) => void |
SubTrigger
An item that opens a submenu. Must be rendered inside MenubarSub
.
Prop | Type | Default |
---|---|---|
as | string | Component | div |
asChild | boolean | false |
disabled | boolean | |
textValue | string |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
SubContent
The component that pops out when a submenu is open. Must be rendered inside MenubarSub
.
Prop | Type | Default |
---|---|---|
loop | boolean | false |
forceMount | boolean | |
sideOffset | number | 0 |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
arrowPadding | number | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
as | string | Component | div |
asChild | boolean | false |
Emit | Type |
---|---|
@escapeKeyDown | (event: KeyboardEvent) => void |
@pointerDownOutside | (event: PointerDownOutsideEvent) => void |
@focusOutside | (event: FocusOutsideEvent) => void |
@interactOutside | (event: FocusEvent | MouseEvent | TouchEvent) => void |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
[data-orientation] | "vertical" | "horizontal" |
CSS Variable | Description |
---|---|
--radix-menubar-content-transform-origin |
The transform-origin computed from the content and arrow positions/offsets
|
--radix-menubar-content-available-width |
The remaining width between the trigger and the boundary edge
|
--radix-menubar-content-available-height |
The remaining height between the trigger and the boundary edge
|
--radix-menubar-trigger-width | The width of the trigger |
--radix-menubar-trigger-height | The height of the trigger |
Examples
With submenus
You can create submenus by using MenubarSub
in combination with its parts.
<script setup lang="ts">
import {
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarSeparator,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Sub menu →</MenubarSubTrigger>
<MenubarPortal>
<MenubarSubContent>
<MenubarItem>Sub menu item</MenubarItem>
<MenubarItem>Sub menu item</MenubarItem>
<MenubarArrow />
</MenubarSubContent>
</MenubarPortal>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>…</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With disabled items
You can add special styles to disabled items via the data-disabled
attribute.
<script setup lang="ts">
import { MenubarContent, MenubarItem, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem class="MenubarItem" disabled>
…
</MenubarItem>
<MenubarItem class="MenubarItem">
…
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarItem[data-disabled] {
color: gainsboro;
}
With separators
Use the Separator
part to add a separator between items.
<script setup lang="ts">
import {
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarSeparator,
MenubarTrigger,
} from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarItem>…</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With labels
Use the Label
part to help label a section.
<script setup lang="ts">
import {
MenubarContent,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarTrigger,
} from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarLabel>Label</MenubarLabel>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With checkbox items
Use the CheckboxItem
part to add an item that can be checked.
<script setup lang="ts">
import {
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarSeparator,
MenubarTrigger,
} from 'radix-vue'
import { Icon } from '@iconify/vue'
const checked = ref(true)
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarCheckboxItem v-model:checked="checked">
<MenubarItemIndicator>
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Checkbox item
</MenubarCheckboxItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With radio items
Use the RadioGroup
and RadioItem
parts to add an item that can be checked amongst others.
<script setup lang="ts">
import {
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup,
MenubarRadioItem,
MenubarRoot,
MenubarSeparator,
MenubarTrigger,
} from 'radix-vue'
import { Icon } from '@iconify/vue'
const color = ref('blue')
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarRadioGroup v-model="color">
<MenubarRadioItem value="red">
<MenubarItemIndicator>
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Red
</MenubarRadioItem>
<MenubarRadioItem value="blue">
<MenubarItemIndicator>
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Blue
</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With complex items
You can add extra decorative elements in the Item
parts, such as images.
<script setup lang="ts">
import { MenubarContent, MenubarItem, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>
<img src="…">
Adolfo Hess
</MenubarItem>
<MenubarItem>
<img src="…">
Miyah Myles
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
Constrain the content/sub-content size
You may want to constrain the width of the content (or sub-content) so that it matches the trigger (or sub-trigger) width. You may also want to constrain its height to not exceed the viewport.
We expose several CSS custom properties such as --radix-menubar-trigger-width
and --radix-menubar-content-available-height
to support this. Use them to constrain the content dimensions.
<script setup lang="ts">
import { MenubarContent, MenubarItem, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger> Trigger </MenubarTrigger>
<MenubarPortal>
<MenubarContent class="MenubarContent" :side-offset="5" :align-offset="-3">
<MenubarItem> New Tab </MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarContent {
width: var(--radix-menubar-trigger-width);
max-height: var(--radix-menubar-content-available-height);
}
Origin-aware animations
We expose a CSS custom property --radix-menubar-content-transform-origin
. Use it to animate the content from its computed origin based on side
, sideOffset
, align
, alignOffset
and any collisions.
<script setup lang="ts">
import { MenubarContent, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent class="MenubarContent">
…
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarContent {
transform-origin: var(--radix-menubar-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
Collision-aware animations
We expose data-side
and data-align
attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
<script setup lang="ts">
import { MenubarContent, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'radix-vue'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent class="MenubarContent">
…
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.MenubarContent[data-side="top"] {
animation-name: slideUp;
}
.MenubarContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Accessibility
Adheres to the Menu Button WAI-ARIA design pattern and uses roving tabindex to manage focus movement among menu items.
Keyboard Interactions
Key | Description |
---|---|
Space |
When focus is on MenubarTrigger , opens the menubar and focuses the first item.
When focus is on an item, activates the focused item. |
Enter |
When focus is on MenubarTrigger , opens the associated menu.
When focus is on an item, activates the focused item. |
ArrowDown |
When focus is on MenubarTrigger , opens the associated menu.
When focus is on an item, moves focus to the next item. |
ArrowUp | When focus is on an item, moves focus to the previous item. |
ArrowRightArrowLeft |
When focus is on a MenubarTrigger , moves focus to the next or previous item. When focus is on a MenubarSubTrigger , opens or closes the submenu depending on reading direction. When focus is within a MenubarContent , opens the next menu in the menubar
|
Esc |
Closes the currently open menu and moves focus to its MenubarTrigger .
|