Skip to content
FrameworkStyle

Time

Time display components for showing current time, duration, and remaining time in a video player

Import

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

Anatomy

<Time.Group>
  <Time.Value type="current" />
  <Time.Separator />
  <Time.Value type="duration" />
</Time.Group>

Behavior

Three display types — current, duration, and remaining — in digital format with smart padding:

  • Hours are never padded (1:05:30, not 01:05:30)
  • Minutes are padded when hours are shown (1:05:30, but 5:30)
  • Seconds are always padded (1:05, not 1:5)

Hour display is triggered when either the current value or the duration exceeds 1 hour, ensuring consistency within a Group. Remaining time displays a negative sign (customizable via the negativeSign prop).

Use toggle to let current displays switch between elapsed and remaining time, or remaining and duration displays switch between those two values. The initial display comes from type.

Before the media reports a duration or seekable range — while metadata is still loading, for example — no time value is available. A plain display still renders the formatted zero value but sets data-unavailable; a toggleable display is disabled instead: it sets data-disabled, leaves the tab order, and ignores activation until a value arrives.

<Time.Value toggle />
<Time.Value toggle type="remaining" />
<Time.Value toggle type="duration" />

Styling

Attribute Values Description
data-type current / duration / remaining The type of time being displayed
data-disabled Present / absent Present when a toggleable display has no time value yet
data-unavailable Present / absent Present when a plain display has no time value yet

The negative sign is rendered inside <span aria-hidden="true"> and can be hidden with CSS:

[data-type="remaining"] > span[aria-hidden] {
  display: none;
}

Dim a display while its value is unavailable:

[data-disabled],
[data-unavailable] {
  opacity: 0.5;
}

Accessibility

Each <Time.Value> has:

  • a native <time datetime> element for machine-readable time semantics
  • aria-label for the static role label (“Current time”, “Duration”, “Remaining”)

No aria-live region is used — time updates too frequently and might overwhelm screen readers. The separator and negative sign are aria-hidden="true" because the accessible label already describes the value.

Toggleable time displays receive role="button", tabindex="0", and keyboard support for Enter and Space. Their aria-label starts with the action and includes the current value. Their aria-description explains which values the control toggles between.

While no time value is available, a toggleable display is disabled: it sets aria-disabled="true" and tabindex="-1", drops the aria-description, changes its aria-label to “Media not loaded, unknown time.”, and ignores clicks and key presses. A plain display in the same state omits datetime and uses that same label.

Examples

Current Time

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

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

export default function CurrentTime() {
  return (
    <Player>
      <Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />
        <Time.Value type="current" className="media-time" />
      </Container>
    </Player>
  );
}

Current / Duration

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

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

export default function CurrentDuration() {
  return (
    <Player>
      <Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />
        <Time.Group className="time-group">
          <Time.Value type="current" />
          <Time.Separator />
          <Time.Value type="duration" />
        </Time.Group>
      </Container>
    </Player>
  );
}

Remaining

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

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

export default function Remaining() {
  return (
    <Player>
      <Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />
        <Time.Value type="remaining" className="media-time" />
      </Container>
    </Player>
  );
}

Custom Separator

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

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

export default function CustomSeparator() {
  return (
    <Player>
      <Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />
        <Time.Group className="time-group">
          <Time.Value type="current" />
          <Time.Separator> of </Time.Separator>
          <Time.Value type="duration" />
        </Time.Group>
      </Container>
    </Player>
  );
}

Custom Negative Sign

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

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

export default function CustomNegativeSign() {
  return (
    <Player>
      <Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />
        <Time.Value type="remaining" negativeSign="~" className="media-time" />
      </Container>
    </Player>
  );
}

API Reference

Value

Displays a formatted time value (current, duration, or remaining).

Props

PropTypeDefaultDetails
labelobject''
negativeSignstring'-'
togglebooleanfalse
type'current' | 'duration' | 'remaining''current'

State

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

PropertyTypeDetails
type'current' | 'duration' | 'remaining'
disabledboolean
unavailableboolean
secondsnumber
negativeboolean
textstring
phrasestring
datetimestring

Data attributes

AttributeTypeDetails
data-type'current' | 'duration' | 'remaining'
data-disabled
data-unavailable

Group

Container for composed time displays. Renders a <span> element.

Props

PropTypeDefaultDetails
childrenReactNode
classNamestring | ((state: GroupState) => string | undefined)
renderReactElement | ((props: HTMLProps, state: GroupState) => ReactElement | null)
styleCSSProperties | ((state: GroupState) => CSSProperties | undefined)

Separator

Divider between time values. Hidden from screen readers.

Props

PropTypeDefaultDetails
childrenReactNode
classNamestring | ((state: SeparatorState) => string | undefined)
renderReactElement | ((props: HTMLProps, state: SeparatorState) => ReactElement | null)
styleCSSProperties | ((state: SeparatorState) => CSSProperties | undefined)