Time
Time display components for showing current time, duration, and remaining time in a video player
Anatomy
<Time.Group>
<Time.Value type="current" />
<Time.Separator />
<Time.Value type="duration" />
</Time.Group><media-time-group>
<media-time type="current"></media-time>
<media-time-separator></media-time-separator>
<media-time type="duration"></media-time>
</media-time-group>Behavior
Three display types — current, duration, and remaining — in digital format with smart padding:
- Hours are never padded (
1:05:30, not01:05:30) - Minutes are padded when hours are shown (
1:05:30, but5:30) - Seconds are always padded (
1:05, not1: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).
Styling
The negative sign is rendered inside <span aria-hidden="true"> and can be hidden with CSS:
[data-type="remaining"] > span[aria-hidden] {
display: none;
}Accessibility
Each <media-time> has:
-
aria-labelfor the static role label (“Current time”, “Duration”, “Remaining”) -
aria-valuetextfor the dynamic human-readable time (“1 minute, 30 seconds”)
Each <Time.Value> has:
-
aria-labelfor the static role label (“Current time”, “Duration”, “Remaining”) -
aria-valuetextfor the dynamic human-readable time (“1 minute, 30 seconds”)
No aria-live region is used — time updates too frequently and might overwhelm screen readers. The separator is aria-hidden="true" since screen readers already hear each time value separately. The negative sign is also aria-hidden because aria-valuetext already conveys “remaining”. In React, <time datetime> provides machine-readable time for parsers.
Examples
Current Time
import { createPlayer, Time } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({ features: videoFeatures });
export default function CurrentTime() {
return (
<Player.Provider>
<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" />
</Player.Container>
</Player.Provider>
);
}
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.media-time {
position: absolute;
bottom: 10px;
left: 10px;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
<video-player class="video-player">
<media-container>
<video
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-time class="media-time" type="current"></media-time>
</media-container>
</video-player>
.video-player {
position: relative;
}
.video-player video {
width: 100%;
}
.media-time {
position: absolute;
bottom: 10px;
left: 10px;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
import '@videojs/html/video/player';
import '@videojs/html/ui/time';
Current / Duration
import { createPlayer, Time } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({ features: videoFeatures });
export default function CurrentDuration() {
return (
<Player.Provider>
<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>
</Player.Container>
</Player.Provider>
);
}
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.time-group {
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
gap: 4px;
align-items: center;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
<video-player class="video-player">
<media-container>
<video
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-time-group class="time-group">
<media-time type="current"></media-time>
<media-time-separator></media-time-separator>
<media-time type="duration"></media-time>
</media-time-group>
</media-container>
</video-player>
.video-player {
position: relative;
}
.video-player video {
width: 100%;
}
.time-group {
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
gap: 4px;
align-items: center;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
import '@videojs/html/video/player';
import '@videojs/html/ui/time';
Remaining
import { createPlayer, Time } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({ features: videoFeatures });
export default function Remaining() {
return (
<Player.Provider>
<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" />
</Player.Container>
</Player.Provider>
);
}
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.media-time {
position: absolute;
bottom: 10px;
left: 10px;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
<video-player class="video-player">
<media-container>
<video
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-time class="media-time" type="remaining"></media-time>
</media-container>
</video-player>
.video-player {
position: relative;
}
.video-player video {
width: 100%;
}
.media-time {
position: absolute;
bottom: 10px;
left: 10px;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
import '@videojs/html/video/player';
import '@videojs/html/ui/time';
Custom Separator
import { createPlayer, Time } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({ features: videoFeatures });
export default function CustomSeparator() {
return (
<Player.Provider>
<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>
</Player.Container>
</Player.Provider>
);
}
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.time-group {
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
gap: 4px;
align-items: center;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
<video-player class="video-player">
<media-container>
<video
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-time-group class="time-group">
<media-time type="current"></media-time>
<media-time-separator> of </media-time-separator>
<media-time type="duration"></media-time>
</media-time-group>
</media-container>
</video-player>
.video-player {
position: relative;
}
.video-player video {
width: 100%;
}
.time-group {
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
gap: 4px;
align-items: center;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
import '@videojs/html/video/player';
import '@videojs/html/ui/time';
Custom Negative Sign
import { createPlayer, Time } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({ features: videoFeatures });
export default function CustomNegativeSign() {
return (
<Player.Provider>
<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" />
</Player.Container>
</Player.Provider>
);
}
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.media-time {
position: absolute;
bottom: 10px;
left: 10px;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
<video-player class="video-player">
<media-container>
<video
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-time
class="media-time"
type="remaining"
negative-sign="~"
></media-time>
</media-container>
</video-player>
.video-player {
position: relative;
}
.video-player video {
width: 100%;
}
.media-time {
position: absolute;
bottom: 10px;
left: 10px;
padding-block: 8px;
padding-inline: 20px;
font-variant-numeric: tabular-nums;
color: black;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
import '@videojs/html/video/player';
import '@videojs/html/ui/time';
API Reference
Value media-time
Displays a formatted time value (current, duration, or remaining).
Props
| Prop | Type | Default | Details |
|---|---|---|---|
formatOptions | TimeFormatOptions | — | |
| |||
label | 'play' | 'pause' | 'replay' | 'mute' | 'unmute' | 'seekForward' | 'seekBackward' | 'enterFullscreen' | 'exitFullscreen' | 'enableCaptions' | 'disableCaptions' | 'enterPictureInPicture' | 'exitPictureInPicture' | 'playingLive' | 'seekToLiveEdge' | 'liveBadge' | 'startCasting' | 'stopCasting' | 'connectingCast' | 'seek' | 'volume' | 'timeCurrent' | 'timeDuration' | 'timeRemaining' | 'timeRemainingPhrase' | 'playbackRateAria' | 'timeSliderValueTextRange' | 'volumeSliderValueTextMuted' | 'indicatorMuted' | 'indicatorVolume' | 'indicatorVolumeWithValue' | 'indicatorCaptionsOn' | 'indicatorCaptionsOff' | 'indicatorPaused' | 'indicatorPlaying' | 'indicatorFullscreen' | 'indicatorExitFullscreen' | 'indicatorPictureInPicture' | 'indicatorExitPictureInPicture' | 'mediaErrorAborted' | 'mediaErrorNetwork' | 'mediaErrorDecode' | 'mediaErrorSrcNotSupported' | 'mediaErrorEncrypted' | 'mediaErrorCustom' | 'errorDialogTitle' | 'errorDialogDismiss' | 'mediaErrorFallback' | string & object | function | '' | |
| |||
negativeSign | string | '-' | |
| |||
type | 'current' | 'duration' | 'remaining' | 'current' | |
| |||
State
render, className, and style props.
| Property | Type | Details |
|---|---|---|
type | 'current' | 'duration' | 'remaining' | |
| ||
seconds | number | |
| ||
negative | boolean | |
| ||
text | string | |
| ||
phrase | string | |
| ||
datetime | string | |
| ||
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-type | 'current' | 'duration' | 'remaining' | |
| ||
Group media-time-group
Container for composed time displays. Renders a <span> element.
Separator media-time-separator
Divider between time values. Hidden from screen readers.