DisplayReact
Controls for an app's own <video> or <audio> element.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<MediaPlayer.Root>
<MediaPlayer.Media />
<MediaPlayer.Controls>
<MediaPlayer.Seek>
<MediaPlayer.SeekControl>
<MediaPlayer.SeekTrack>
<MediaPlayer.SeekBuffered />
<MediaPlayer.SeekIndicator />
<MediaPlayer.SeekThumb />
</MediaPlayer.SeekTrack>
</MediaPlayer.SeekControl>
</MediaPlayer.Seek>
<MediaPlayer.PlayButton />
<MediaPlayer.SeekButton />
<MediaPlayer.MuteButton />
<MediaPlayer.Volume>
<MediaPlayer.VolumeControl>
<MediaPlayer.VolumeTrack>
<MediaPlayer.VolumeIndicator />
<MediaPlayer.VolumeThumb />
</MediaPlayer.VolumeTrack>
</MediaPlayer.VolumeControl>
</MediaPlayer.Volume>
<MediaPlayer.Time />
<MediaPlayer.CaptionsButton />
<MediaPlayer.PictureInPictureButton />
<MediaPlayer.FullscreenButton />
</MediaPlayer.Controls>
<MediaPlayer.Error />
</MediaPlayer.Root>Examples
Audio
render puts an <audio> element in place of the video: the seek bar, Play, the times, Mute and Volume, with no picture.
API reference
79 props across 22 parts, in React syntax. className, style, render are listed once, under Shared props.
CaptionsButtonMediaPlayer.CaptionsButton
Shows and hides the captions or subtitles; named "Show captions" or "Hide captions". Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
focusableWhenDisabledWhether the button stays focusable while disabled. | boolean | true |
| Data attribute | Description |
|---|---|
data-showing | Present while a captions or subtitles track is shown. |
data-disabled | Present when the button is disabled. |
data-unavailable | Present when the feature is not available for this media or browser. |
ControlsMediaPlayer.Controls
Holds the controls; the pointer over it keeps the player from going idle. Not a toolbar: each control is a Tab stop. Renders a <div>.
| Data attribute | Description |
|---|---|
data-idle | Present while the player is playing without activity for idleTimeout. |
ErrorMediaPlayer.Error
Says what stopped the media, as an alert, only while there is a media error; the children or the label of the error. Renders a <div>.
| Data attribute | Description |
|---|---|
data-error | The media error. |
FullscreenButtonMediaPlayer.FullscreenButton
Enters and exits fullscreen on the Root; named "Enter fullscreen" or "Exit fullscreen". Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
focusableWhenDisabledWhether the button stays focusable while disabled. | boolean | true |
| Data attribute | Description |
|---|---|
data-fullscreen | Present while the player is fullscreen. |
data-disabled | Present when the button is disabled. |
data-unavailable | Present when the feature is not available for this media or browser. |
MediaMediaPlayer.Media
The app's own media element, which the player reads and controls. Every attribute and child passes through as written.; render={<audio />} for audio. Renders a <video>.
MuteButtonMediaPlayer.MuteButton
Mutes and unmutes the media; named "Mute" or "Unmute". Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
focusableWhenDisabledWhether the button stays focusable while disabled. | boolean | true |
| Data attribute | Description |
|---|---|
data-muted | Present while the media is muted. |
data-disabled | Present when the button is disabled. |
data-unavailable | Present when the feature is not available for this media or browser. |
PictureInPictureButtonMediaPlayer.PictureInPictureButton
Enters and exits picture-in-picture; named "Enter picture-in-picture" or "Exit picture-in-picture". Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
focusableWhenDisabledWhether the button stays focusable while disabled. | boolean | true |
| Data attribute | Description |
|---|---|
data-picture-in-picture | Present while the video is in picture-in-picture. |
data-disabled | Present when the button is disabled. |
data-unavailable | Present when the feature is not available for this media or browser. |
PlayButtonMediaPlayer.PlayButton
Plays the media while it is paused and pauses it otherwise; named "Play" or "Pause". Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
focusableWhenDisabledWhether the button stays focusable while disabled. | boolean | true |
| Data attribute | Description |
|---|---|
data-playing | Present while the media is playing. |
data-paused | Present while the media is paused. |
data-ended | Present while the media has ended. |
data-disabled | Present when the button is disabled. |
data-unavailable | Present when the feature is not available for this media or browser. |
RootMediaPlayer.Root
Groups all parts of the media player: the app's own media element and the controls bound to it., with the status region as its last child. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
volumeThe volume, from 0 to 1. | number | — |
defaultVolumeThe volume written to the element on mount; without it the element's own volume stands. | number | — |
onVolumeChangeEvent handler called when the volume changes, before a part writes it; cancel() skips the | ChangeHandler<number> | — |
mutedWhether the media is muted. | boolean | — |
defaultMutedWhether the media is muted on mount; without it the element's own muted stands. | boolean | — |
onMutedChangeEvent handler called when muted changes. | ChangeHandler<boolean> | — |
playbackRateThe playback rate, written to defaultPlaybackRate too so a new source keeps it. | number | — |
defaultPlaybackRateThe playback rate on mount; without it the element's own rate stands. | number | — |
onPlaybackRateChangeEvent handler called when the playback rate changes. | ChangeHandler<number> | — |
textTrackThe id of the shown captions or subtitles track, or null for none. | string | null | — |
defaultTextTrackThe track shown once it exists; without it the browser's automatic selection stands. | string | null | — |
onTextTrackChangeEvent handler called when the shown captions or subtitles track changes. | ChangeHandler<string | null> | — |
onTextTracksChangeEvent handler called with the captions and subtitles tracks, on mount and when one is added | ChangeHandler<readonly MediaPlayerTextTrack[]> | — |
onPlayingChangeEvent handler called after the element started or stopped playing. | ChangeHandler<boolean> | — |
onCurrentTimeChangeEvent handler called after the element's current time changed, in seconds. | ChangeHandler<number> | — |
onFullscreenChangeEvent handler called after the player entered or left fullscreen. | ChangeHandler<boolean> | — |
onPictureInPictureChangeEvent handler called after the video entered or left picture-in-picture. | ChangeHandler<boolean> | — |
onDurationChangeEvent handler called after the element's duration changed, in seconds. | ChangeHandler<number> | — |
onPlayErrorEvent handler called when play() is rejected for another reason than an interruption. | ((error: unknown, eventDetails: MediaPlayerRootChangeEventDetails) => void) | — |
onErrorEvent handler called when the element reports a media error. | ChangeHandler<MediaPlayerError> | — |
defaultCurrentTimeThe start position in seconds, applied once at the first loadedmetadata. | number | 0 |
seekStepThe seconds the arrow-key shortcuts seek by. | number | 5 |
volumeStepThe volume the arrow-key shortcuts change by. | number | 0.05 |
keyboardShortcutsWhether the keyboard shortcuts act while focus is inside the player. | boolean | true |
idleTimeoutThe milliseconds without activity before a playing player is idle; 0 never. | number | 2000 |
disabledWhether the component should ignore user interaction. | boolean | false |
localeThe locale of the times and the volume percent. | Intl.LocalesArgument | — |
labelsThe names and texts of the parts, in English by default. | Partial<MediaPlayerLabels> | — |
actionsRefA ref to imperative actions: play(), pause(), togglePlaying(), seek(time), | React.RefObject<MediaPlayerActions | null> | — |
| Data attribute | Description |
|---|---|
data-playing | Present while the media is playing. |
data-paused | Present while the media is paused. |
data-ended | Present once the media has ended. |
data-loading | Present while a source is set and its metadata is not loaded. |
data-waiting | Present while playback waits for data. |
data-seeking | Present while the media is seeking. |
data-error | Present while there is a media error. |
data-muted | Present while the media is muted. |
data-fullscreen | Present while the player is fullscreen. |
data-picture-in-picture | Present while the video is in picture-in-picture. |
data-captions | Present while a captions or subtitles track is shown. |
data-idle | Present while the player is playing without activity for idleTimeout. |
data-live | Present while the media is a live stream. |
data-disabled | Present when the player is disabled. |
SeekMediaPlayer.Seek
The seek bar: Slider's Root with the current time as its value and the duration as its maximum. Disabled until the duration is known and while there is an error. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
disabledWhether the slider should ignore user interaction. | boolean | false |
localeThe locale used by Intl.NumberFormat when formatting the value. | Intl.LocalesArgument | — |
orientationThe component orientation. | Orientation | 'horizontal' |
stepThe granularity with which the slider can step through values. | number | 1 |
largeStepThe granularity with which the slider can step through values when using Page Up/Page Down or Shift + Arrow Up/Arrow Down. | number | 10 |
thumbAlignmentHow the thumb(s) are aligned relative to Slider.Control when the value is at min or max: | "center" | "edge" | "edge-client-only" | 'center' |
onValueChangeCallback function that is fired when the slider's value changed. | ((value: number, eventDetails: SliderRootChangeEventDetails) => void) | — |
onValueCommittedCallback function that is fired when a value change is committed. | ((value: number, eventDetails: SliderRootCommitEventDetails) => void) | — |
SeekBufferedMediaPlayer.SeekBuffered
How far the media is buffered from the current time, as a width (or a height when vertical) and --media-buffered. Place it inside the SeekTrack. Renders a <div>.
| CSS variable | Description |
|---|---|
--media-buffered | How far the media is buffered from the current time, as a percent of the duration. |
SeekButtonMediaPlayer.SeekButton
Seeks by amount seconds, clamped to the media; named "Forward {time}" or "Back {time}". Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
amountThe seconds to seek by; a negative amount seeks back. | number | 10 |
focusableWhenDisabledWhether the button stays focusable while disabled. | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when the button is disabled. |
data-unavailable | Present when the feature is not available for this media or browser. |
SeekControlMediaPlayer.SeekControl
The clickable, interactive part of the slider. Renders a <div>.
| Data attribute | Description |
|---|---|
data-dragging | Present while the user is dragging. |
data-orientation | Indicates the orientation of the slider. |
data-disabled | Present when the slider is disabled. |
data-valid | Present when the slider is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the slider is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the slider has been touched (when wrapped in Field.Root). |
data-dirty | Present when the slider's value has changed (when wrapped in Field.Root). |
data-focused | Present when the slider is focused (when wrapped in Field.Root). |
SeekIndicatorMediaPlayer.SeekIndicator
Visualizes the current value of the slider. Renders a <div>.
| Data attribute | Description |
|---|---|
data-dragging | Present while the user is dragging. |
data-orientation | Indicates the orientation of the slider. |
data-disabled | Present when the slider is disabled. |
data-valid | Present when the slider is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the slider is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the slider has been touched (when wrapped in Field.Root). |
data-dirty | Present when the slider's value has changed (when wrapped in Field.Root). |
data-focused | Present when the slider is focused (when wrapped in Field.Root). |
SeekThumbMediaPlayer.SeekThumb
The seek bar's thumb, with a native <input type="range"> named "Seek" whose value text is "{current} of {duration}". Renders a <div>.
| Prop | Type | Default |
|---|---|---|
disabledWhether the thumb should ignore user interaction. | boolean | false |
aria-valuetextA string value forwarded to the [aria-valuetext](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-valuetext) attribute of the input. | string | — |
getAriaLabelA function which returns a string value for the [aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label) attribute of the input. | ((index: number) => string) | null | — |
getAriaValueTextA function which returns a string value for the [aria-valuetext](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-valuetext) attribute of the input. | ((formattedValue: string, value: number, index: number) => string) | null | — |
indexThe index of the thumb which corresponds to the index of its value in the | number | — |
inputRefA ref to access the nested input element. | React.Ref<HTMLInputElement> | — |
onBlurA blur handler forwarded to the input. | React.FocusEventHandler<HTMLInputElement> | — |
onFocusA focus handler forwarded to the input. | React.FocusEventHandler<HTMLInputElement> | — |
onKeyDownA keydown handler forwarded to the input. | React.KeyboardEventHandler<HTMLInputElement> | — |
tabIndexOptional tab index attribute forwarded to the input. | number | — |
SeekTrackMediaPlayer.SeekTrack
Contains the slider indicator and represents the entire range of the slider. Renders a <div>.
| Data attribute | Description |
|---|---|
data-dragging | Present while the user is dragging. |
data-orientation | Indicates the orientation of the slider. |
data-disabled | Present when the slider is disabled. |
data-valid | Present when the slider is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the slider is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the slider has been touched (when wrapped in Field.Root). |
data-dirty | Present when the slider's value has changed (when wrapped in Field.Root). |
data-focused | Present when the slider is focused (when wrapped in Field.Root). |
TimeMediaPlayer.Time
The current time, the duration or the time remaining: the digits, hidden from assistive technology, and a visually hidden long form. Renders a <time>.
| Prop | Type | Default |
|---|---|---|
typeWhich time to show: the current time, the duration or the time remaining. | MediaPlayerTimeType | 'current' |
| Data attribute | Description |
|---|---|
data-type | Which time is shown. |
data-unknown | Present while the duration is unknown. |
VolumeMediaPlayer.Volume
The volume slider: Slider's Root from 0 to 1, at 0 while muted. Disabled with data-unavailable where the volume cannot be set. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
stepThe granularity with which the volume changes with the arrow keys and a pointer. | number | 0.05 |
largeStepThe volume change with Shift and an arrow key, or with Page Up and Page Down. | number | 0.1 |
disabledWhether the slider should ignore user interaction. | boolean | false |
localeThe locale used by Intl.NumberFormat when formatting the value. | Intl.LocalesArgument | — |
orientationThe component orientation. | Orientation | 'horizontal' |
thumbAlignmentHow the thumb(s) are aligned relative to Slider.Control when the value is at min or max: | "center" | "edge" | "edge-client-only" | 'center' |
onValueChangeCallback function that is fired when the slider's value changed. | ((value: number, eventDetails: SliderRootChangeEventDetails) => void) | — |
onValueCommittedCallback function that is fired when a value change is committed. | ((value: number, eventDetails: SliderRootCommitEventDetails) => void) | — |
VolumeControlMediaPlayer.VolumeControl
The clickable, interactive part of the slider. Renders a <div>.
| Data attribute | Description |
|---|---|
data-dragging | Present while the user is dragging. |
data-orientation | Indicates the orientation of the slider. |
data-disabled | Present when the slider is disabled. |
data-valid | Present when the slider is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the slider is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the slider has been touched (when wrapped in Field.Root). |
data-dirty | Present when the slider's value has changed (when wrapped in Field.Root). |
data-focused | Present when the slider is focused (when wrapped in Field.Root). |
VolumeIndicatorMediaPlayer.VolumeIndicator
Visualizes the current value of the slider. Renders a <div>.
| Data attribute | Description |
|---|---|
data-dragging | Present while the user is dragging. |
data-orientation | Indicates the orientation of the slider. |
data-disabled | Present when the slider is disabled. |
data-valid | Present when the slider is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the slider is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the slider has been touched (when wrapped in Field.Root). |
data-dirty | Present when the slider's value has changed (when wrapped in Field.Root). |
data-focused | Present when the slider is focused (when wrapped in Field.Root). |
VolumeThumbMediaPlayer.VolumeThumb
The volume slider's thumb, with a native <input type="range"> named "Volume" whose value text is the locale's percent, or "Muted". Renders a <div>.
| Prop | Type | Default |
|---|---|---|
disabledWhether the thumb should ignore user interaction. | boolean | false |
aria-valuetextA string value forwarded to the [aria-valuetext](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-valuetext) attribute of the input. | string | — |
getAriaLabelA function which returns a string value for the [aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label) attribute of the input. | ((index: number) => string) | null | — |
getAriaValueTextA function which returns a string value for the [aria-valuetext](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-valuetext) attribute of the input. | ((formattedValue: string, value: number, index: number) => string) | null | — |
indexThe index of the thumb which corresponds to the index of its value in the | number | — |
inputRefA ref to access the nested input element. | React.Ref<HTMLInputElement> | — |
onBlurA blur handler forwarded to the input. | React.FocusEventHandler<HTMLInputElement> | — |
onFocusA focus handler forwarded to the input. | React.FocusEventHandler<HTMLInputElement> | — |
onKeyDownA keydown handler forwarded to the input. | React.KeyboardEventHandler<HTMLInputElement> | — |
tabIndexOptional tab index attribute forwarded to the input. | number | — |
VolumeTrackMediaPlayer.VolumeTrack
Contains the slider indicator and represents the entire range of the slider. Renders a <div>.
| Data attribute | Description |
|---|---|
data-dragging | Present while the user is dragging. |
data-orientation | Indicates the orientation of the slider. |
data-disabled | Present when the slider is disabled. |
data-valid | Present when the slider is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the slider is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the slider has been touched (when wrapped in Field.Root). |
data-dirty | Present when the slider's value has changed (when wrapped in Field.Root). |
data-focused | Present when the slider is focused (when wrapped in Field.Root). |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| Spacek | Plays or pauses | Root |
| m | Mutes or unmutes | Root |
| c | Shows or hides the captions | Root |
| f | Enters or exits fullscreen | Root |
| ArrowLeftArrowRight | Seeks back or forward by seekStep (5 seconds) | Root |
| ArrowUpArrowDown | Turns the volume up or down by volumeStep (0.05) | Root |
| HomeEnd | Seeks to the start or the end | Root |
| 09 | Seeks to that tenth of the duration | Root |
| With focus on a control, acts the same | Root | |
| SpaceEnter | On a button, presses only that button | PlayButton |
On the seek bar, 1 or 10 seconds; Home, End the ends | SeekThumb | |
| On the volume slider, 0.05 or 0.1 | VolumeThumb |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Root | role="group" named "Media player" (labels.player or aria-label), tabindex="-1" | the controls are announced as one player, and the player is no extra Tab stop |
| Root | a polite status region as its last child | a shortcut says what it did ("Muted", "Captions on"); a press or a slider key does not repeat its own announcement |
| PlayButton | a button named "Play" or "Pause", disabled (still focusable) without a source or after an error | the name says what a press will do |
| MuteButton | a button named "Mute" or "Unmute" | the same for the sound |
| CaptionsButton | a button named "Show captions" or "Hide captions", data-unavailable without a captions track | the same for the captions |
| PictureInPictureButton | a button named "Enter picture-in-picture" or "Exit picture-in-picture" | the same for the floating window |
| FullscreenButton | a button named "Enter fullscreen" or "Exit fullscreen" | the same for fullscreen |
| SeekButton | named "Back 10 seconds" or "Forward 10 seconds" from its amount | the direction and the step are read |
| SeekThumb | a native <input type="range"> named "Seek", its value text the long time ("1 minute, 5 seconds of 3 minutes") | the position is read as a time, not as a number of seconds |
| VolumeThumb | a native <input type="range"> named "Volume", its value text a percentage | the level is read as a percentage |
| SeekBuffered | aria-hidden="true" | the loaded range is not read |
| Time | a <time> with a visually hidden long form and aria-hidden digits | "1:05" is read as "1 minute, 5 seconds" |
| Error | role="alert", rendered only while there is an error | the error is announced when it happens |
Focus order
- The Root is not a Tab stop; each control is one, in DOM order. The Controls are not a toolbar, because the two sliders use the arrow keys themselves.
- A press on the video or on the Controls' background focuses the Root when focus is outside the player, so the keys work after a click.
- A control the media disables (PlayButton after an error) keeps focus.