PlaybackRateRadioGroup
A menu radio group for selecting playback speed
Creates radio items from the player playback rate state and selects the playback speed.
Import
import { PlaybackRateRadioGroup } from '@videojs/react';import '@videojs/html/ui/playback-rate-radio-group';Anatomy
<PlaybackRateRadioGroup.Root>
<PlaybackRateRadioGroup.Value />
<PlaybackRateRadioGroup.Options
renderItem={(props, item) => (
<Menu.RadioItem {...props}>
{item.label}
<Menu.ItemIndicator checked={item.checked} />
</Menu.RadioItem>
)}
/>
</PlaybackRateRadioGroup.Root><media-playback-rate-radio-group>
<template>
<media-menu-radio-item>
<span data-part="label"></span>
<media-menu-item-indicator></media-menu-item-indicator>
</media-menu-radio-item>
</template>
</media-playback-rate-radio-group>Behavior
The group is available when the configured media exposes playback rates. Option labels default to the rate followed by a multiplication sign (e.g. 1.5×); pass formatRate to customize them.
PlaybackRateRadioGroup.Root owns selection and publishes option state to an enclosing Menu. Value displays the selected label. Options generates each item and returns null when the playback rate feature is not configured.
<media-playback-rate-radio-group> generates <media-menu-radio-item> children. Add an optional <template> with a single <media-menu-radio-item> to customize each generated item.
Styling
| Attribute | Values | Description |
|---|---|---|
data-rate |
number |
Current playback rate. |
data-disabled |
Present / absent | Present when playback rate selection is disabled. |
data-hidden |
Present / absent | Present when playback-rate selection is unavailable. |
data-availability |
"available" / "unavailable" |
Whether playback rates are available. |
Unavailable groups receive the native hidden attribute.
Accessibility
The group uses the menu radio group pattern.
The component receives an accessible label from the label prop or defaults to Playback rate. Override it with aria-label or aria-labelledby.
The element receives an accessible label from the label property or defaults to Playback rate. Override it with aria-label or aria-labelledby.
Examples
Basic usage
import { Container, createPlayer, Menu, PlaybackRateRadioGroup } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
import type { ReactNode } from 'react';
const { Player } = createPlayer({ features: videoFeatures });
function SpeedMenu(): ReactNode {
return (
<Menu.Root side="top" align="end">
<Menu.Trigger className="settings-trigger" render={<button type="button" />}>
Speed
</Menu.Trigger>
<Menu.Popup className="menu">
<Menu.Content>
<PlaybackRateRadioGroup.Root>
<PlaybackRateRadioGroup.Options
className="menu-group"
renderItem={(props, item) => (
<Menu.RadioItem {...props} className="menu-item">
{item.label}
<Menu.ItemIndicator checked={item.checked} forceMount className="menu-indicator">
✓
</Menu.ItemIndicator>
</Menu.RadioItem>
)}
/>
</PlaybackRateRadioGroup.Root>
</Menu.Content>
</Menu.Popup>
</Menu.Root>
);
}
export default function BasicUsage() {
return (
<Player>
<Container className="media-container">
<Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />
<div className="menu-bar">
<SpeedMenu />
</div>
</Container>
</Player>
);
}
.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;
box-sizing: border-box;
display: grid;
gap: 2px;
min-width: 180px;
max-width: var(--media-menu-available-width, var(--media-popover-available-width, none));
max-height: var(--media-menu-available-height, var(--media-popover-available-height, none));
padding: 6px;
margin: 0;
overflow: auto;
overscroll-behavior: none;
font-size: 14px;
color: white;
background: rgba(0, 0, 0, 0.88);
border: 0;
border-radius: 8px;
backdrop-filter: blur(10px);
}
.menu-group {
display: grid;
gap: 2px;
}
.menu-item {
display: flex;
gap: 8px;
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] {
background: rgba(255, 255, 255, 0.16);
}
.menu-indicator {
opacity: 0;
}
[role="menuitemradio"][aria-checked="true"] .menu-indicator {
opacity: 1;
}
<video-player class="video-player">
<media-container>
<video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoplay muted playsinline loop></video>
<div class="menu-bar">
<button type="button" commandfor="speed-menu" class="settings-trigger">Speed</button>
<media-menu id="speed-menu" side="top" align="end" class="menu">
<media-menu-content>
<media-playback-rate-radio-group class="menu-group" aria-label="Speed">
<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-content>
</media-menu>
</div>
</media-container>
</video-player>
.video-player media-container {
position: relative;
display: block;
}
.video-player 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;
box-sizing: border-box;
display: grid;
gap: 2px;
min-width: 180px;
max-width: var(--media-menu-available-width, var(--media-popover-available-width, none));
max-height: var(--media-menu-available-height, var(--media-popover-available-height, none));
padding: 6px;
overflow: auto;
overscroll-behavior: none;
font-size: 14px;
color: white;
background: rgba(0, 0, 0, 0.88);
border-radius: 8px;
backdrop-filter: blur(10px);
}
.menu-group {
display: grid;
gap: 2px;
}
.menu-item {
display: flex;
gap: 8px;
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] {
background: rgba(255, 255, 255, 0.16);
}
.menu-indicator {
opacity: 0;
}
media-menu-radio-item[aria-checked="true"] .menu-indicator {
opacity: 1;
}
import '@videojs/html/video/player';
import '@videojs/html/ui/container';
import '@videojs/html/ui/menu';
import '@videojs/html/ui/playback-rate-radio-group';
API Reference
Valuemedia-playback-rate-radio-group
Displays the selected playback-rate label.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: PlaybackRateOptionsResult) => string | undefined) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: PlaybackRateOptionsResult) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: PlaybackRateOptionsResult) => CSSProperties | undefined) | — | |
| |||
Events
| Event | Description |
|---|---|
value-change | Fired when the selected value changes. |