Menu
A composable menu component for settings, option selection, and actions
Anatomy
A submenu is a menu inside another menu. The submenu trigger lives in the parent list, and the submenu content is the next view.
<Menu.Root>
<Menu.Trigger />
<Menu.Content>
<Menu.View>
{/* Parent menu item that opens a submenu */}
<Menu.Root>
{/* Submenu trigger */}
<Menu.Trigger>
<Menu.ItemValue />
</Menu.Trigger>
{/* Submenu content */}
<Menu.Content>
<Menu.Back />
<Menu.RadioGroup>
<Menu.GroupLabel />
<Menu.RadioItem>
<Menu.ItemIndicator />
</Menu.RadioItem>
</Menu.RadioGroup>
</Menu.Content>
</Menu.Root>
{/* Other parent menu items */}
<Menu.Group>
<Menu.GroupLabel />
<Menu.CheckboxItem>
<Menu.ItemIndicator />
</Menu.CheckboxItem>
</Menu.Group>
<Menu.Separator />
<Menu.Item />
</Menu.View>
</Menu.Content>
</Menu.Root>
<button type="button" commandfor="settings-menu">Settings</button>
<media-menu id="settings-menu">
<media-menu-view>
<!-- Parent menu item that opens a submenu -->
<media-menu-item commandfor="submenu">
<media-menu-item-value></media-menu-item-value>
</media-menu-item>
<!-- Other parent menu items -->
<media-menu-group>
<media-menu-group-label></media-menu-group-label>
<media-menu-checkbox-item>
<media-menu-item-indicator></media-menu-item-indicator>
</media-menu-checkbox-item>
</media-menu-group>
<media-menu-separator></media-menu-separator>
<media-menu-item></media-menu-item>
</media-menu-view>
<!-- Submenu content -->
<media-menu id="submenu">
<media-menu-back></media-menu-back>
<media-menu-radio-group>
<media-menu-group-label></media-menu-group-label>
<media-menu-radio-item>
<media-menu-item-indicator></media-menu-item-indicator>
</media-menu-radio-item>
</media-menu-radio-group>
</media-menu>
</media-menu>
Behavior
Menus open from a trigger and close when you select an item, click outside, move focus away, or press Escape. Root menus are positioned against their trigger with side and align.
Create a submenu by putting another menu inside the root menu’s content. Its trigger stays in the root list, and its content becomes the next view. In HTML, give the submenu a matching id and point the trigger at it with commandfor. Wrap the root list in Menu.View or <media-menu-view> when the root list and submenus share one animated viewport.
Set type="playback-rate", type="quality", type="audio-track", or type="captions" on a submenu trigger when it represents a media setting. Menu.ItemValue and <media-menu-item-value> read that setting context and display the current value, such as 1.5x, Auto, English, or Off. See QualityRadioGroup and AudioTrackRadioGroup for the generated option groups.
Styling
Use data attributes to style open state, highlighted items, selected radio items, and submenu views:
data-highlighted is added to the current menu item. The highlighted item is the item that keyboard selection will act on. It changes when the menu opens, when the pointer moves over another item, when the user presses arrow keys, or when type-ahead search finds a match.
.menu[data-open] {
opacity: 1;
}
.menu-item[data-highlighted] {
background: rgba(255, 255, 255, 0.16);
}
[role="menuitemradio"][aria-checked="true"] {
font-weight: 600;
}
media-menu[data-open] {
opacity: 1;
}
media-menu-item[data-highlighted] {
background: rgba(255, 255, 255, 0.16);
}
media-menu-radio-item[aria-checked="true"] {
font-weight: 600;
}
Accessibility
Menu content renders with role="menu". Items use menuitem, menuitemradio, or menuitemcheckbox roles. Radio and checkbox items reflect selection with aria-checked.
Keyboard controls:
- Enter / Space: Select the highlighted item.
- Arrow Up / Arrow Down: Move between items.
- Arrow Right: Open a submenu.
- Arrow Left: Return to the parent menu.
- Escape: Close the root menu or return from a submenu.
Use Menu.GroupLabel or <media-menu-group-label> inside grouped choices so the group receives an accessible label.
Examples
Basic usage
import {
createPlayer,
Menu,
useAudioTrackOptions,
useCaptionsOptions,
usePlaybackRateOptions,
useQualityOptions,
} from '@videojs/react';
import { HlsJsVideo } from '@videojs/react/media/hlsjs-video';
import { videoFeatures } from '@videojs/react/video';
import type { ReactNode } from 'react';
const Player = createPlayer({ features: videoFeatures });
const src = 'https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4.m3u8';
function SettingsMenu(): ReactNode {
const playbackRate = usePlaybackRateOptions();
const quality = useQualityOptions();
const audioTrack = useAudioTrackOptions();
const captions = useCaptionsOptions();
const hasPlaybackRate = playbackRate?.state.availability === 'available';
const hasQuality = quality?.state.availability === 'available';
const hasAudioTrack = audioTrack?.state.availability === 'available';
const hasCaptions = captions?.state.availability === 'available';
if (!hasPlaybackRate && !hasQuality && !hasAudioTrack && !hasCaptions) return null;
return (
<Menu.Root side="top" align="end">
<Menu.Trigger className="settings-trigger" aria-label="Settings" render={<button type="button" />}>
Settings
</Menu.Trigger>
<Menu.Content className="menu">
<Menu.View className="menu-panel">
{hasQuality ? (
<Menu.Root>
<Menu.Trigger
type="quality"
className="menu-item"
render={(props) => (
<div {...props}>
<span>Quality</span>
<span className="menu-value">
<Menu.ItemValue />
<span aria-hidden="true">›</span>
</span>
</div>
)}
/>
<Menu.Content className="menu-panel">
<Menu.Back className="menu-back">
<span aria-hidden="true">‹</span>
Quality
</Menu.Back>
<Menu.RadioGroup
className="menu-group"
value={quality.value}
onValueChange={quality.setValue}
aria-label="Quality"
>
{quality.options.map((option) => (
<Menu.RadioItem
key={option.value}
value={option.value}
disabled={option.disabled}
className="menu-item"
>
<span>
{option.label}
{option.tier ? <sup className="menu-tier">{option.tier}</sup> : null}
</span>
{option.badge ? <span className="menu-badge">{option.badge}</span> : null}
<Menu.ItemIndicator
checked={option.value === quality.value}
forceMount
className="menu-indicator"
>
✓
</Menu.ItemIndicator>
</Menu.RadioItem>
))}
</Menu.RadioGroup>
</Menu.Content>
</Menu.Root>
) : null}
{hasAudioTrack ? (
<Menu.Root>
<Menu.Trigger
type="audio-track"
className="menu-item"
render={(props) => (
<div {...props}>
<span>Audio</span>
<span className="menu-value">
<Menu.ItemValue />
<span aria-hidden="true">›</span>
</span>
</div>
)}
/>
<Menu.Content className="menu-panel">
<Menu.Back className="menu-back">
<span aria-hidden="true">‹</span>
Audio
</Menu.Back>
<Menu.RadioGroup
className="menu-group"
value={audioTrack.value}
onValueChange={audioTrack.setValue}
aria-label="Audio tracks"
>
{audioTrack.options.map((option) => (
<Menu.RadioItem
key={option.value}
value={option.value}
disabled={option.disabled}
className="menu-item"
>
<span>{option.label}</span>
<Menu.ItemIndicator
checked={option.value === audioTrack.value}
forceMount
className="menu-indicator"
>
✓
</Menu.ItemIndicator>
</Menu.RadioItem>
))}
</Menu.RadioGroup>
</Menu.Content>
</Menu.Root>
) : null}
{hasPlaybackRate ? (
<Menu.Root>
<Menu.Trigger
type="playback-rate"
className="menu-item"
render={(props) => (
<div {...props}>
<span>Speed</span>
<span className="menu-value">
<Menu.ItemValue />
<span aria-hidden="true">›</span>
</span>
</div>
)}
/>
<Menu.Content className="menu-panel">
<Menu.Back className="menu-back">
<span aria-hidden="true">‹</span>
Speed
</Menu.Back>
<Menu.RadioGroup
className="menu-group"
value={playbackRate.value}
onValueChange={playbackRate.setValue}
aria-label="Playback rate"
>
{playbackRate.options.map((option) => (
<Menu.RadioItem
key={option.value}
value={option.value}
disabled={option.disabled}
className="menu-item"
>
<span>{option.label}</span>
<Menu.ItemIndicator
checked={option.value === playbackRate.value}
forceMount
className="menu-indicator"
>
✓
</Menu.ItemIndicator>
</Menu.RadioItem>
))}
</Menu.RadioGroup>
</Menu.Content>
</Menu.Root>
) : null}
{hasCaptions ? (
<Menu.Root>
<Menu.Trigger
type="captions"
className="menu-item"
render={(props) => (
<div {...props}>
<span>Captions</span>
<span className="menu-value">
<Menu.ItemValue />
<span aria-hidden="true">›</span>
</span>
</div>
)}
/>
<Menu.Content className="menu-panel">
<Menu.Back className="menu-back">
<span aria-hidden="true">‹</span>
Captions
</Menu.Back>
<Menu.RadioGroup
className="menu-group"
value={captions.value}
onValueChange={captions.setValue}
aria-label="Captions"
>
{captions.options.map((option) => (
<Menu.RadioItem
key={option.value}
value={option.value}
disabled={option.disabled}
className="menu-item"
>
<span>{option.label}</span>
<Menu.ItemIndicator
checked={option.value === captions.value}
forceMount
className="menu-indicator"
>
✓
</Menu.ItemIndicator>
</Menu.RadioItem>
))}
</Menu.RadioGroup>
</Menu.Content>
</Menu.Root>
) : null}
<Menu.Item className="menu-item" onSelect={() => navigator.clipboard?.writeText(window.location.href)}>
Copy link
</Menu.Item>
</Menu.View>
</Menu.Content>
</Menu.Root>
);
}
export default function BasicUsage() {
return (
<Player.Provider>
<Player.Container className="media-container">
<HlsJsVideo src={src} autoPlay crossOrigin="anonymous" muted playsInline loop>
<track kind="captions" src="/docs/demos/captions-button/captions.vtt" srcLang="en" label="English" />
<track kind="subtitles" src="/docs/demos/captions-button/captions.vtt" srcLang="es" label="Spanish" />
</HlsJsVideo>
<div className="menu-bar">
<SettingsMenu />
</div>
</Player.Container>
</Player.Provider>
);
}
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.menu-bar {
position: absolute;
right: 10px;
bottom: 10px;
}
.settings-trigger {
padding: 6px 16px;
color: black;
cursor: pointer;
background: rgba(255, 255, 255, 0.75);
border: 1px solid rgba(255, 255, 255, 0.35);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
.menu {
--media-menu-side-offset: 8px;
--menu-transition-duration: 220ms;
position: relative;
box-sizing: border-box;
width: var(--media-menu-width);
min-width: 180px;
max-width: var(--media-menu-available-width, var(--media-popover-available-width, none));
height: var(--media-menu-height);
max-height: var(--media-menu-available-height, var(--media-popover-available-height, none));
padding: 6px;
margin: 0;
overflow: hidden;
overscroll-behavior: none;
font-size: 14px;
color: white;
background: rgba(0, 0, 0, 0.88);
border: 0;
border-radius: 8px;
backdrop-filter: blur(10px);
transition-timing-function: ease-in-out;
transition-duration: var(--menu-transition-duration);
transition-property: width, height, opacity, filter;
}
.menu-panel {
position: absolute;
inset: 0;
display: grid;
gap: 2px;
padding: 6px;
overflow: auto;
overscroll-behavior: none;
outline: none;
translate: 0 0;
transition-timing-function: ease-in-out;
transition-duration: var(--menu-transition-duration);
transition-property: translate, filter;
will-change: translate;
}
.menu-panel[data-starting-style],
.menu-panel[data-ending-style] {
overflow: hidden;
}
.menu-panel[data-menu-root-view][data-menu-view-state="inactive"] {
filter: blur(8px);
translate: -100% 0;
}
.menu-panel[data-submenu] {
z-index: 1;
}
.menu-panel[data-submenu]:not([data-open], [data-ending-style]) {
translate: -100% 0;
transition-property: none;
}
.menu-panel[data-submenu][data-starting-style],
.menu-panel[data-submenu][data-ending-style] {
pointer-events: none;
filter: blur(8px);
}
.menu-panel[data-submenu][data-starting-style][data-direction="forward"],
.menu-panel[data-submenu][data-ending-style][data-direction="back"] {
translate: 100% 0;
}
.menu-panel[data-submenu][data-ending-style][data-direction="forward"],
.menu-panel[data-submenu][data-starting-style][data-direction="back"] {
translate: -100% 0;
}
.menu-item,
.menu-back {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 32px;
padding: 0 10px;
font: inherit;
color: inherit;
cursor: pointer;
background: none;
border: 0;
border-radius: 6px;
}
.menu-item[data-highlighted],
.menu-back:hover {
background: rgba(255, 255, 255, 0.16);
}
.menu-back {
gap: 8px;
justify-content: flex-start;
}
.menu-value {
display: inline-flex;
gap: 8px;
align-items: center;
color: rgba(255, 255, 255, 0.72);
}
.menu-group {
display: grid;
gap: 2px;
}
.menu-tier {
margin-left: 2px;
font-size: 10px;
}
.menu-badge {
margin-left: auto;
color: rgba(255, 255, 255, 0.72);
}
.menu-back [aria-hidden],
[role="menuitemradio"] [aria-hidden],
.menu-item [aria-hidden] {
font-size: 18px;
line-height: 1;
color: rgba(255, 255, 255, 0.72);
}
.menu-indicator {
opacity: 0;
}
[role="menuitemradio"][aria-checked="true"] .menu-indicator {
opacity: 1;
}
<video-player class="video-player">
<media-container>
<hlsjs-video
class="video-media"
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4.m3u8"
autoplay
crossorigin="anonymous"
muted
playsinline
loop
>
<track kind="captions" src="/docs/demos/captions-button/captions.vtt" srclang="en" label="English" />
<track kind="subtitles" src="/docs/demos/captions-button/captions.vtt" srclang="es" label="Spanish" />
</hlsjs-video>
<div class="menu-bar">
<button type="button" commandfor="settings-menu" class="settings-trigger">Settings</button>
<media-menu id="settings-menu" side="top" align="end" class="menu">
<media-menu-view class="menu-panel">
<media-menu-item commandfor="quality-menu" type="quality" class="menu-item">
<span>Quality</span>
<span class="menu-value">
<media-menu-item-value></media-menu-item-value>
<span aria-hidden="true">›</span>
</span>
</media-menu-item>
<media-menu-item commandfor="audio-menu" type="audio-track" class="menu-item">
<span>Audio</span>
<span class="menu-value">
<media-menu-item-value></media-menu-item-value>
<span aria-hidden="true">›</span>
</span>
</media-menu-item>
<media-menu-item commandfor="speed-menu" type="playback-rate" class="menu-item">
<span>Speed</span>
<span class="menu-value">
<media-menu-item-value></media-menu-item-value>
<span aria-hidden="true">›</span>
</span>
</media-menu-item>
<media-menu-item commandfor="captions-menu" type="captions" class="menu-item">
<span>Captions</span>
<span class="menu-value">
<media-menu-item-value></media-menu-item-value>
<span aria-hidden="true">›</span>
</span>
</media-menu-item>
<media-menu-item class="menu-item">Copy link</media-menu-item>
</media-menu-view>
<media-menu id="quality-menu" class="menu-panel">
<media-menu-back class="menu-back">
<span aria-hidden="true">‹</span>
Quality
</media-menu-back>
<media-quality-radio-group class="menu-group">
<template>
<media-menu-radio-item class="menu-item">
<span>
<span data-part="label"></span>
<sup data-part="tier" class="menu-tier"></sup>
</span>
<span data-part="badge" class="menu-badge"></span>
<media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
</media-menu-radio-item>
</template>
</media-quality-radio-group>
</media-menu>
<media-menu id="audio-menu" class="menu-panel">
<media-menu-back class="menu-back">
<span aria-hidden="true">‹</span>
Audio
</media-menu-back>
<media-audio-track-radio-group class="menu-group">
<template>
<media-menu-radio-item class="menu-item">
<span data-part="label"></span>
<media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
</media-menu-radio-item>
</template>
</media-audio-track-radio-group>
</media-menu>
<media-menu id="speed-menu" class="menu-panel">
<media-menu-back class="menu-back">
<span aria-hidden="true">‹</span>
Speed
</media-menu-back>
<media-playback-rate-radio-group class="menu-group">
<template>
<media-menu-radio-item class="menu-item">
<span data-part="label"></span>
<media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
</media-menu-radio-item>
</template>
</media-playback-rate-radio-group>
</media-menu>
<media-menu id="captions-menu" class="menu-panel">
<media-menu-back class="menu-back">
<span aria-hidden="true">‹</span>
Captions
</media-menu-back>
<media-captions-radio-group class="menu-group">
<template>
<media-menu-radio-item class="menu-item">
<span data-part="label"></span>
<media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
</media-menu-radio-item>
</template>
</media-captions-radio-group>
</media-menu>
</media-menu>
</div>
</media-container>
</video-player>
.video-player,
.video-player media-container {
position: relative;
display: block;
}
.video-media {
width: 100%;
}
.menu-bar {
position: absolute;
right: 10px;
bottom: 10px;
}
.settings-trigger {
padding: 6px 16px;
color: black;
cursor: pointer;
background: rgba(255, 255, 255, 0.75);
border: 1px solid rgba(255, 255, 255, 0.35);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
.menu {
--media-menu-side-offset: 8px;
--menu-transition-duration: 220ms;
position: relative;
box-sizing: border-box;
width: var(--media-menu-width);
min-width: 180px;
max-width: var(--media-menu-available-width, var(--media-popover-available-width, none));
height: var(--media-menu-height);
max-height: var(--media-menu-available-height, var(--media-popover-available-height, none));
padding: 6px;
overflow: hidden;
overscroll-behavior: none;
font-size: 14px;
color: white;
background: rgba(0, 0, 0, 0.88);
border-radius: 8px;
backdrop-filter: blur(10px);
transition-timing-function: ease-in-out;
transition-duration: var(--menu-transition-duration);
transition-property: width, height, opacity, filter;
}
.menu-panel {
position: absolute;
inset: 0;
display: grid;
gap: 2px;
padding: 6px;
overflow: auto;
overscroll-behavior: none;
outline: none;
translate: 0 0;
transition-timing-function: ease-in-out;
transition-duration: var(--menu-transition-duration);
transition-property: translate, filter;
will-change: translate;
}
.menu-panel[data-starting-style],
.menu-panel[data-ending-style] {
overflow: hidden;
}
.menu-panel[data-menu-root-view][data-menu-view-state="inactive"] {
filter: blur(8px);
translate: -100% 0;
}
.menu-panel[data-submenu] {
z-index: 1;
}
.menu-panel[data-submenu]:not([data-open], [data-ending-style]) {
translate: -100% 0;
transition-property: none;
}
.menu-panel[data-submenu][data-starting-style],
.menu-panel[data-submenu][data-ending-style] {
pointer-events: none;
filter: blur(8px);
}
.menu-panel[data-submenu][data-starting-style][data-direction="forward"],
.menu-panel[data-submenu][data-ending-style][data-direction="back"] {
translate: 100% 0;
}
.menu-panel[data-submenu][data-ending-style][data-direction="forward"],
.menu-panel[data-submenu][data-starting-style][data-direction="back"] {
translate: -100% 0;
}
.menu-item,
.menu-back {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 32px;
padding: 0 10px;
font: inherit;
color: inherit;
cursor: pointer;
background: none;
border: 0;
border-radius: 6px;
}
.menu-item[data-highlighted],
.menu-back:hover {
background: rgba(255, 255, 255, 0.16);
}
.menu-back {
gap: 8px;
justify-content: flex-start;
}
.menu-item[data-availability="unavailable"] {
display: none;
}
.menu-value {
display: inline-flex;
gap: 8px;
align-items: center;
color: rgba(255, 255, 255, 0.72);
}
.menu-value [aria-hidden] {
font-size: 18px;
line-height: 1;
}
.menu-back [aria-hidden] {
font-size: 18px;
line-height: 1;
color: rgba(255, 255, 255, 0.72);
}
.menu-group {
display: grid;
gap: 2px;
}
.menu-tier {
margin-left: 2px;
font-size: 10px;
}
.menu-badge {
margin-left: auto;
color: rgba(255, 255, 255, 0.72);
}
.menu-indicator {
opacity: 0;
}
media-menu-radio-item[aria-checked="true"] .menu-indicator {
opacity: 1;
}
import '@videojs/html/video/player';
import '@videojs/html/media/hlsjs-video';
import '@videojs/html/ui/menu';
import '@videojs/html/ui/quality-radio-group';
import '@videojs/html/ui/audio-track-radio-group';
import '@videojs/html/ui/playback-rate-radio-group';
import '@videojs/html/ui/captions-radio-group';
API Reference
Root
media-menu
Props
| Prop | Type | Default | Details |
|---|---|---|---|
align
|
'start' | 'center' | 'end'
|
'start'
|
|
|
|||
closeOnEscape
|
boolean
|
true
|
|
|
|||
closeOnOutsideClick
|
boolean
|
true
|
|
|
|||
defaultOpen
|
boolean
|
false
|
|
|
|||
isSubmenu
|
boolean
|
false
|
|
|
|||
open
|
boolean
|
false
|
|
|
|||
side
|
'top' | 'bottom' | 'left' | 'right'
|
'bottom'
|
|
|
|||
State
render, className, and style props.
| Property | Type | Details |
|---|---|---|
open
|
boolean
|
|
status
|
'idle' | 'starting' | 'ending'
|
|
side
|
'top' | 'bottom' | 'left' | 'right' |...
|
|
|
||
align
|
'start' | 'center' | 'end' | undefined
|
|
isSubmenu
|
boolean
|
|
|
||
transitionStarting
|
boolean
|
|
|
||
transitionEnding
|
boolean
|
|
|
||
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-open
|
||
|
||
data-side
|
undefined | 'top' | 'bottom' | 'left'...
|
|
|
||
data-align
|
undefined | 'start' | 'center' | 'end'
|
|
|
||
data-submenu
|
||
|
||
CSS custom properties
| Variable | Details |
|---|---|
--media-menu-width
|
|
|
|
--media-menu-height
|
|
|
|
--media-menu-available-width
|
|
|
|
--media-menu-available-height
|
|
|
|
Back
media-menu-back
Button that navigates back to the parent menu view. Place at the top of a submenu Content.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
label
|
string
|
—
|
|
|
|||
CheckboxItem
media-menu-checkbox-item
A checkbox-style menu item. Renders a <div> with role="menuitemcheckbox".
Props
| Prop | Type | Default | Details |
|---|---|---|---|
checked
|
boolean
|
—
|
|
|
|||
disabled
|
boolean
|
—
|
|
|
|||
onCheckedChange
|
(checked: boolean) => void
|
—
|
|
|
|||
Content
Content
Container for menu items. Positioned relative to the trigger at root level; renders in-place as a submenu panel when nested.
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-open
|
||
|
||
data-side
|
undefined | 'top' | 'bottom' | 'left'...
|
|
|
||
data-align
|
undefined | 'start' | 'center' | 'end'
|
|
|
||
data-submenu
|
||
|
||
Group
media-menu-group
Groups related menu items. Renders a <div> with role="group".
GroupLabel
media-menu-group-label
Non-interactive label for a group of items. Renders a <div>.
Item
media-menu-item
A single action in the menu. Renders a <div> with role="menuitem".
Props
| Prop | Type | Default | Details |
|---|---|---|---|
disabled
|
boolean
|
—
|
|
|
|||
onSelect
|
() => void
|
—
|
|
|
|||
type
|
MenuItemSettingType
|
—
|
|
|
|||
ItemIndicator
media-menu-item-indicator
Visual indicator for a checked state. Only renders when checked is true (or forceMount is set).
Props
| Prop | Type | Default | Details |
|---|---|---|---|
checked
|
boolean
|
—
|
|
|
|||
forceMount
|
boolean
|
—
|
|
|
|||
ItemValue
media-menu-item-value
Displays the current value for a settings menu item from Menu.Item or Menu.Trigger context.
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-open
|
||
|
||
data-side
|
undefined | 'top' | 'bottom' | 'left'...
|
|
|
||
data-align
|
undefined | 'start' | 'center' | 'end'
|
|
|
||
data-submenu
|
||
|
||
RadioGroup
media-menu-radio-group
A group of mutually exclusive radio items. Renders a <div> with role="group".
Props
| Prop | Type | Default | Details |
|---|---|---|---|
onValueChange
|
(value: string) => void
|
—
|
|
|
|||
value
|
string
|
—
|
|
|
|||
RadioItem
media-menu-radio-item
A radio-style menu item. Renders a <div> with role="menuitemradio".
Props
| Prop | Type | Default | Details |
|---|---|---|---|
disabled
|
boolean
|
—
|
|
|
|||
value
|
string
|
—
|
|
|
|||
Separator
media-menu-separator
Visual divider between groups of items. Renders a <div> with role="separator".
Trigger
Trigger
Button that toggles the menu visibility. At root level renders a <button>.
When inside a parent menu (as a submenu trigger), renders as a <div role="menuitem">
that pushes the submenu on click or ArrowRight.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
disabled
|
boolean
|
—
|
|
|
|||
type
|
MenuItemSettingType
|
—
|
|
|
|||
View
media-menu-view
Root menu view inside the menu viewport.