Skip to content
FrameworkStyle

VolumeIndicator

Display temporary visual feedback for keyboard and gesture volume actions

Import

import { VolumeIndicator } from '@videojs/react';

Anatomy

<VolumeIndicator.Root>
  <VolumeIndicator.Fill>
    <VolumeIndicator.Value />
  </VolumeIndicator.Fill>
</VolumeIndicator.Root>

Behavior

VolumeIndicator displays feedback for toggleMuted and volumeStep actions emitted by a Hotkey or Gesture in the same player Container. It does not react to mute buttons, volume sliders, direct player-store changes, or volume changes on their own.

The input event arrives before its action is resolved. The indicator uses the media snapshot from that moment to predict the next mute state and volume. toggleMuted predicts the opposite mute state. volumeStep adds its value to the snapshot volume and clamps the result from 0 to 1. A step whose clamped result is above 0 also predicts that mute will clear.

The Root exposes the predicted level:

data-level Predicted state
"off" Muted or volume is 0
"low" Unmuted volume is greater than 0 and at most 0.5
"high" Unmuted volume is greater than 0.5

Value displays the rounded percentage from 0% through 100%. Fill receives the same percentage through --media-volume-fill. Nest Value inside Fill so the progress treatment and text form one visual unit.

The indicator closes after closeDelay, which defaults to 800 milliseconds. Repeated handled actions update the current value and restart that close timer without replaying the entry transition. Each update uses the latest media snapshot; the component does not independently accumulate volume steps. React stops rendering the Root after its close transition. The HTML custom element remains in the document with hidden until the next handled action.

When a nonzero volumeStep cannot move past an already-clamped edge, data-min or data-max is present for 300 milliseconds. Hitting the same edge again briefly clears the attribute, then restores it on the next task so a CSS boundary animation can restart. Merely reaching 0% or 100% does not trigger the boundary attribute until another step tries to move farther.

Styling

Attribute Values Description
data-open Present / absent Present while the indicator is open
data-level "off", "low", or "high" Predicted volume level
data-min Present / absent Present briefly after a blocked downward step at minimum volume
data-max Present / absent Present briefly after a blocked upward step at maximum volume
data-starting-style Present / absent Present during the open transition
data-ending-style Present / absent Present during the close transition

--media-volume-fill is set on the Fill part, not the Root. Read it from a Fill selector when sizing a progress treatment.

React renders standard DOM elements. Add separate className values to the Root and Fill:

.volume-indicator__fill::before {
  width: var(--media-volume-fill, 0%);
}

.volume-indicator[data-starting-style],
.volume-indicator[data-ending-style] {
  opacity: 0;
}

Accessibility

VolumeIndicator is visual feedback and does not create a live region. Keep volume and mute available through keyboard-operable controls, and pair the player with StatusAnnouncer when state changes should be announced to screen readers. Do not make VolumeIndicator.Value a live region.

Examples

Basic Usage

Focus the player, then press M to mute or unmute. Use Arrow Up and Arrow Down to adjust volume by 5%.

Focus the player · M: mute · ↑/↓: volume ±5%

import { Container, createPlayer, Hotkey, VolumeIndicator } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';

import './BasicUsage.css';

const { Player } = createPlayer({ features: videoFeatures });

export default function BasicUsage() {
  return (
    <Player>
      <Container className="react-volume-indicator-basic" tabIndex={0}>
        <Video
          ref={(video) => {
            if (video) video.volume = 0.5;
          }}
          src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
          autoPlay
          muted
          playsInline
          loop
        />
        <p className="react-volume-indicator-basic__instructions">Focus the player · M: mute · ↑/↓: volume ±5%</p>
        <VolumeIndicator.Root className="react-volume-indicator-basic__indicator" aria-hidden="true">
          <VolumeIndicator.Fill className="react-volume-indicator-basic__fill">
            <VolumeIndicator.Value className="react-volume-indicator-basic__value" />
          </VolumeIndicator.Fill>
        </VolumeIndicator.Root>
        <Hotkey keys="m" action="toggleMuted" />
        <Hotkey keys="ArrowUp" action="volumeStep" value={0.05} />
        <Hotkey keys="ArrowDown" action="volumeStep" value={-0.05} />
      </Container>
    </Player>
  );
}

API Reference

Root

Props

PropTypeDefaultDetails
closeDelaynumber
labelsPartial<InputIndicatorLabels>

State

State is accessible via the render, className, and style props.

PropertyTypeDetails
transitionStartingboolean
transitionEndingboolean
openboolean
generationnumber
level'off' | 'low' | 'high' | null
valuestring | null
fillstring | null
minboolean
maxboolean

Data attributes

AttributeTypeDetails
data-open
data-level'off' | 'low' | 'high' | null
data-min
data-max
data-starting-style
data-ending-style

CSS custom properties

VariableDetails
--media-volume-fill

Fill

Props

PropTypeDefaultDetails
classNamestring | ((state: VolumeIndicatorCore.State) => string | undefined)
renderReactElement | ((props: HTMLProps, state: VolumeIndicatorCore.State) => ReactElement | null)
styleCSSProperties | ((state: VolumeIndicatorCore.State) => CSSProperties | undefined)

Value

Props

PropTypeDefaultDetails
classNamestring | ((state: VolumeIndicatorCore.State) => string | undefined)
renderReactElement | ((props: HTMLProps, state: VolumeIndicatorCore.State) => ReactElement | null)
styleCSSProperties | ((state: VolumeIndicatorCore.State) => CSSProperties | undefined)