FrameworkStyle

Thumbnail

Time-based thumbnail preview component for timeline scrubbing and hover previews

Quick Start: Video Track

Thumbnail can read thumbnail cues directly from your video track. Add a <track> with kind="metadata" and label="thumbnails" to your media element.

Mux provides this as storyboard.vtt:

https://image.mux.com/{PLAYBACK_ID}/storyboard.vtt

<Video src="video.mp4">
  <track
    kind="metadata"
    label="thumbnails"
    src="https://image.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/storyboard.vtt"
    default
  />
</Video>
<Thumbnail time={12} />

Anatomy

<Thumbnail time={12} />

Behavior

Thumbnail resolves an image for the current time.

Supported source formats:

  • Text track: <track kind="metadata" label="thumbnails" src="...vtt">
  • JSON array: { url, startTime, endTime? }[]
  • JSON sprite array: { url, startTime, endTime?, width, height, coords }[]

In React, text-track mode needs Player.Provider because it reads track state from the player store. JSON modes (thumbnails prop) work without Provider.

The component picks the latest thumbnail whose startTime is less than or equal to the current time, then scales/clips sprite tiles to fit CSS min/max constraints while preserving aspect ratio.

Styling

Use state data attributes for pure CSS styling:

media-thumbnail[data-hidden] {
  display: none;
}

media-thumbnail[data-loading] {
  opacity: 0.6;
}

media-thumbnail[data-error] {
  outline: 1px solid #ef4444;
}

Accessibility

Thumbnail is decorative by default (aria-hidden="true"). It is intended for visual preview UX (for example, timeline hover previews) rather than primary accessible content.

Examples

Text Track (VTT)

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

import './BasicUsage.css';

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

export default function TextTrackUsage() {
  return (
    <Player.Provider>
      <Player.Container className="react-thumbnail-text-track">
        <Video
          className="react-thumbnail-text-track__media"
          src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
          preload="auto"
          muted
          playsInline
          crossOrigin="anonymous"
        >
          <track kind="metadata" label="thumbnails" src="/docs/demos/thumbnail/basic.vtt" default />
        </Video>
        <Thumbnail className="react-thumbnail-text-track__thumbnail" time={12} />
      </Player.Container>
    </Player.Provider>
  );
}

JSON Array

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

const THUMBNAILS = [
  {
    url: 'https://image.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/thumbnail.jpg?time=0',
    startTime: 0,
    endTime: 10,
  },
  {
    url: 'https://image.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/thumbnail.jpg?time=10',
    startTime: 10,
    endTime: 20,
  },
  {
    url: 'https://image.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/thumbnail.jpg?time=20',
    startTime: 20,
  },
];

export default function JsonUsage() {
  return <Thumbnail thumbnails={THUMBNAILS} time={12} style={{ maxWidth: 240 }} />;
}

JSON Sprite Array

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

const THUMBNAILS = [
  {
    url: 'https://image.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/storyboard.jpg',
    startTime: 0,
    endTime: 10,
    width: 284,
    height: 160,
    coords: { x: 0, y: 0 },
  },
  {
    url: 'https://image.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/storyboard.jpg',
    startTime: 10,
    endTime: 20,
    width: 284,
    height: 160,
    coords: { x: 284, y: 0 },
  },
  {
    url: 'https://image.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/storyboard.jpg',
    startTime: 20,
    width: 284,
    height: 160,
    coords: { x: 568, y: 0 },
  },
];

export default function JsonSpriteUsage() {
  return <Thumbnail thumbnails={THUMBNAILS} time={12} style={{ maxWidth: 240 }} />;
}

API Reference

Props

Prop Type Default
crossOrigin 'anonymous' | 'use-credentials' | '' ...
fetchPriority 'high' | 'low' | 'auto'
loading 'eager' | 'lazy'
time number

State

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

Property Type
loading boolean
error boolean
hidden boolean

Data attributes

Attribute Type
data-loading
data-error
data-hidden
VideoJS