Menubar
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { ref } from 'vue'
import {
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup,
MenubarRadioItem,
MenubarRoot,
MenubarSeparator,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from 'radix-vue'
const currentMenu = ref('')
const checkboxOne = ref(false)
const checkboxTwo = ref(false)
const person = ref('pedro')
function handleClick() {
alert('hello!')
}
const RADIO_ITEMS = ['Andy', 'Benoît', 'Luis']
const CHECK_ITEMS = ['Always Show Bookmarks Bar', 'Always Show Full URLs']
</script>
<template>
<MenubarRoot v-model="currentMenu" class="flex bg-white p-[3px] rounded-md shadow-[0_2px_10px] shadow-blackA7">
<MenubarMenu value="file">
<MenubarTrigger
class="py-2 px-3 outline-none select-none font-semibold leading-none rounded text-grass11 text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-green4 data-[state=open]:bg-green4"
>
File
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="min-w-[220px] outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] [animation-duration:_400ms] [animation-timing-function:_cubic-bezier(0.16,_1,_0.3,_1)] will-change-[transform,opacity]"
align="start"
:side-offset="5"
:align-offset="-3"
>
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
New Tab
<div class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
⌘ T
</div>
</MenubarItem>
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
New Window
<div class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
⌘ N
</div>
</MenubarItem>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
disabled
>
New Incognito Window
</MenubarItem>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarSub>
<MenubarSubTrigger
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Share
<div
class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</MenubarSubTrigger>
<MenubarPortal>
<MenubarSubContent
class="min-w-[220px] outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] [animation-duration:_400ms] [animation-timing-function:_cubic-bezier(0.16,_1,_0.3,_1)] will-change-[transform,opacity]"
:align-offset="-5"
>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Email Link
</MenubarItem>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Messages
</MenubarItem>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Notes
</MenubarItem>
</MenubarSubContent>
</MenubarPortal>
</MenubarSub>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Print…
<div class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
⌘ P
</div>
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
<MenubarMenu value="Edit">
<MenubarTrigger
class="py-2 px-3 outline-none select-none font-semibold leading-none rounded text-grass11 text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-green4 data-[state=open]:bg-green4"
>
Edit
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="min-w-[220px] outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] [animation-duration:_400ms] [animation-timing-function:_cubic-bezier(0.16,_1,_0.3,_1)] will-change-[transform,opacity]"
align="start"
:side-offset="5"
:align-offset="-3"
>
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Undo
<div class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
⌘ Z
</div>
</MenubarItem>
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Redo
<div class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
⇧ ⌘ Z
</div>
</MenubarItem>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarSub>
<MenubarSubTrigger
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Find
<div
class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</MenubarSubTrigger>
<MenubarPortal>
<MenubarSubContent
class="min-w-[220px] outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] [animation-duration:_400ms] [animation-timing-function:_cubic-bezier(0.16,_1,_0.3,_1)] will-change-[transform,opacity]"
:align-offset="-5"
>
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Search the web…
</MenubarItem>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Find…
</MenubarItem>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Find Next
</MenubarItem>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Find Previous
</MenubarItem>
</MenubarSubContent>
</MenubarPortal>
</MenubarSub>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Cut
</MenubarItem>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Copy
</MenubarItem>
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Paste
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
<MenubarMenu value="View">
<MenubarTrigger
class="py-2 px-3 outline-none select-none font-semibold leading-none rounded text-grass11 text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-green4 data-[state=open]:bg-green4"
>
View
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="min-w-[220px] outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] [animation-duration:_400ms] [animation-timing-function:_cubic-bezier(0.16,_1,_0.3,_1)] will-change-[transform,opacity]"
align="start"
:side-offset="5"
:align-offset="-14"
>
<MenubarCheckboxItem
v-model="checkboxOne"
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[20px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
<MenubarItemIndicator class="absolute left-0 w-[20px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Show Bookmarks
<div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+B
</div>
</MenubarCheckboxItem>
<MenubarCheckboxItem
v-model="checkboxTwo"
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[20px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
<MenubarItemIndicator class="absolute left-0 w-[20px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Show Full URLs
</MenubarCheckboxItem>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none pl-5 outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Reload
<div class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
⌘ R
</div>
</MenubarItem>
<MenubarItem
class="group text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none pl-5 outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
disabled
>
Force Reload
<div class="ml-auto pl-5 text-mauve9 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
⇧ ⌘ R
</div>
</MenubarItem>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none pl-5 outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Toggle Fullscreen
</MenubarItem>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none pl-5 outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Hide Sidebar
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
<MenubarMenu value="Profiles">
<MenubarTrigger
class="py-2 px-3 outline-none select-none font-semibold leading-none rounded text-grass11 text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-green4 data-[state=open]:bg-green4"
>
Profiles
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="min-w-[220px] outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] [animation-duration:_400ms] [animation-timing-function:_cubic-bezier(0.16,_1,_0.3,_1)] will-change-[transform,opacity]"
align="start"
:side-offset="5"
:align-offset="-14"
>
<MenubarRadioGroup v-model="person">
<MenubarRadioItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[20px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
value="pedro"
>
<MenubarItemIndicator class="absolute left-0 w-[20px] inline-flex items-center justify-center">
<Icon icon="radix-icons:dot-filled" />
</MenubarItemIndicator>
Pedro Duarte
</MenubarRadioItem>
<MenubarRadioItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[20px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
value="colm"
>
<MenubarItemIndicator class="absolute left-0 w-[20px] inline-flex items-center justify-center">
<Icon icon="radix-icons:dot-filled" />
</MenubarItemIndicator>
Colm Tuite
</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none pl-5 outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
@click="handleClick"
>
Edit…
</MenubarItem>
<MenubarSeparator class="h-[1px] bg-green6 m-[5px]" />
<MenubarItem
class="text-[13px] leading-none text-grass11 rounded flex items-center h-[25px] px-[10px] relative select-none pl-5 outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[highlighted]:bg-gradient-to-br data-[highlighted]:from-green9 data-[highlighted]:to-green10 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:text-green1 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none"
>
Add Profile…
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
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 | Default | Type |
---|---|---|
defaultValue | string The value of the menu that should be open when initially rendered. Use when you do not need to control the value state. | |
dir | 'ltr' | 'rtl' The reading direction of the combobox when applicable. If omitted, inherits globally from | |
loop | false | boolean When |
modelValue | string The controlled value of the menu to open. Can be used as |
Emit | Payload |
---|---|
update:modelValue | [value: boolean] Event handler called when the value changes. |
Menu
A top level menu item, contains a trigger with content combination.
Prop | Default | Type |
---|---|---|
value | string A unique value that associates the item with an active value when the navigation menu is controlled. This prop is managed automatically when uncontrolled. |
Trigger
The button that toggles the content. By default, the MenubarContent
will position itself against the trigger.
Prop | Default | Type |
---|---|---|
as | 'button' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | boolean When |
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 | Default | Type |
---|---|---|
disabled | boolean Disable teleport and render the component inline | |
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
to | string | HTMLElement Vue native teleport component prop |
Content
The component that pops out when a menu is open.
Prop | Default | Type |
---|---|---|
align | 'start' | 'start' | 'center' | 'end' The preferred alignment against the trigger. May change when collisions occur. |
alignOffset | number An offset in pixels from the | |
arrowPadding | number The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. | |
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
avoidCollisions | boolean When | |
collisionBoundary | Element | (Element | null)[] | null The element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. | |
collisionPadding | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>> The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. | |
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideWhenDetached | boolean Whether to hide the content when the trigger becomes fully occluded. | |
loop | boolean When | |
prioritizePosition | boolean Force content to be position within the viewport. Might overlap the reference element, which may not be desired. | |
side | 'top' | 'right' | 'bottom' | 'left' The preferred side of the trigger to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. | |
sideOffset | number The distance in pixels from the trigger. | |
sticky | 'partial' | 'always' The sticky behavior on the align axis. | |
updatePositionStrategy | 'always' | 'optimized' Strategy to update the position of the floating element on every animation frame. |
Emit | Payload |
---|---|
closeAutoFocus | [event: Event] Event handler called when auto-focusing on close. Can be prevented. |
escapeKeyDown | [event: KeyboardEvent] Event handler called when the escape key is down. Can be prevented. |
focusOutside | [event: FocusOutsideEvent] Event handler called when the focus moves outside of the |
interactOutside | [event: PointerDownOutsideEvent | FocusOutsideEvent] Event handler called when an interaction happens outside the |
pointerDownOutside | [event: PointerDownOutsideEvent] Event handler called when the a |
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 | Default | Type |
---|---|---|
as | 'svg' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
height | 5 | number The height of the arrow in pixels. |
width | 10 | number The width of the arrow in pixels. |
Item
The component that contains the menubar items.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | boolean When | |
textValue | string Optional text used for typeahead purposes. By default the typeahead behavior will use the |
Emit | Payload |
---|---|
select | [event: Event] Event handler called when the user selects an item (via mouse or keyboard). |
Data Attribute | Value |
---|---|
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Group
Used to group multiple MenubarItem
s.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Label
Used to render a label. It won't be focusable using arrow keys.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
CheckboxItem
An item that can be controlled and rendered like a checkbox.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
checked | false | true | 'indeterminate' The controlled checked state of the item. Can be used as | |
disabled | boolean When | |
textValue | string Optional text used for typeahead purposes. By default the typeahead behavior will use the |
Emit | Payload |
---|---|
select | [event: Event] Event handler called when the user selects an item (via mouse or keyboard). |
update:checked | [payload: boolean] Event handler called when the checked state changes. |
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 | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
modelValue | string The value of the selected item in the group. |
Emit | Payload |
---|---|
update:modelValue | [payload: boolean] Event handler called when the value changes. |
RadioItem
An item that can be controlled and rendered like a radio.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | boolean When | |
textValue | string Optional text used for typeahead purposes. By default the typeahead behavior will use the | |
value* | string The unique value of the item. |
Emit | Payload |
---|---|
select | [event: Event] Event handler called when the user selects an item (via mouse or keyboard). |
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 | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" |
Separator
Used to visually separate items in a menubar menu.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Sub
Contains all the parts of a submenu.
Prop | Default | Type |
---|---|---|
defaultOpen | boolean The open state of the submenu when it is initially rendered. Use when you do not need to control its open state. | |
open | boolean The controlled open state of the menu. Can be used as |
Emit | Payload |
---|---|
update:open | [payload: boolean] Event handler called when the open state of the submenu changes. |
SubTrigger
An item that opens a submenu. Must be rendered inside MenubarSub
.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | boolean When | |
textValue | string Optional text used for typeahead purposes. By default the typeahead behavior will use the |
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 | Default | Type |
---|---|---|
alignOffset | number An offset in pixels from the | |
arrowPadding | number The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. | |
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
avoidCollisions | boolean When | |
collisionBoundary | Element | (Element | null)[] | null The element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. | |
collisionPadding | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>> The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. | |
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideWhenDetached | boolean Whether to hide the content when the trigger becomes fully occluded. | |
loop | boolean When | |
prioritizePosition | boolean Force content to be position within the viewport. Might overlap the reference element, which may not be desired. | |
sideOffset | number The distance in pixels from the trigger. | |
sticky | 'partial' | 'always' The sticky behavior on the align axis. | |
updatePositionStrategy | 'always' | 'optimized' Strategy to update the position of the floating element on every animation frame. |
Emit | Payload |
---|---|
closeAutoFocus | [event: Event] Event handler called when auto-focusing on close. Can be prevented. |
entryFocus | [event: Event] |
escapeKeyDown | [event: KeyboardEvent] Event handler called when the escape key is down. Can be prevented. |
focusOutside | [event: FocusOutsideEvent] Event handler called when the focus moves outside of the |
interactOutside | [event: PointerDownOutsideEvent | FocusOutsideEvent] Event handler called when an interaction happens outside the |
openAutoFocus | [event: Event] Event handler called when auto-focusing on open. Can be prevented. |
pointerDownOutside | [event: PointerDownOutsideEvent] Event handler called when the a |
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 .
|