Skip to content
FrameworkStyle

Installation

Install Video.js and build your first player with streaming support and accessible controls

Video.js is an HTML video player built on custom elements — lightweight, framework-free components for building accessible, customizable players with minimal bundle size.

Answer the questions below to get started quickly with your first embed code.

Choose your JS framework

Video.js aims to provide idiomatic development experiences in your favorite JS and CSS frameworks. More to come.

React
HTML

Choose your use case

The default presets work well for general website playback. More pre-built players to come.

Video
Audio
Background Video

Choose skin

Choose how your player looks.

Default
Minimal

Want full control over the skin? You can eject it copy the source into your project and customize it freely.

Choose your media source type

Video.js supports a wide range of file types and hosting services. It’s easy to switch between them.

Select your source

Or upload your media for free to Mux

Drop a video— or —

Install Video.js

<script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/html/cdn/video.js"></script>

Use your player

<!--
  The PlayerProvider passes state between the UI components
  and Media, and makes fully custom UIs possible.
  It does not have layout by default (display:contents)
 -->
<video-player>
  <!--
    Skins contain the entire player UI and are easily swappable.
    They can each be "ejected" for full control and customization
    of UI components.
   -->
  <video-skin>
    <!--
      Media are players without UIs, handling networking
      and display of the media. They are easily swappable
      to handle different sources.
    -->
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" playsinline></video>
  </video-skin>
</video-player>

Ejecting a skin

Skins ship pre-built. To customize beyond CSS variables, eject — copy the skin source into your project and own it from there. Pick whichever built-in skin is closest to your goal as a starting point.

Default
Minimal
<script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/html/cdn/video-ui.js"></script>
<link rel="stylesheet" href="./player.css">

<video-player>
  <media-container class="media-default-skin media-default-skin--video">
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" playsinline></video>

    <media-poster>
      <img src="https://image.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/thumbnail.webp" />
    </media-poster>

    <media-buffering-indicator class="media-buffering-indicator">
      <div class="media-surface">
        <media-icon name="spinner" class="media-icon"></media-icon>
      </div>
    </media-buffering-indicator>

    <media-error-dialog class="media-error">
      <div class="media-error__dialog media-surface">
        <div class="media-error__content">
          <media-alert-dialog-title class="media-error__title">Something went wrong.</media-alert-dialog-title>
          <media-alert-dialog-description class="media-error__description"></media-alert-dialog-description>
        </div>
        <div class="media-error__actions">
          <media-alert-dialog-close class="media-button media-button--primary">OK</media-alert-dialog-close>
        </div>
      </div>
    </media-error-dialog>

    <media-controls class="media-surface media-controls">
      <media-tooltip-group>
        <div class="media-button-group">
          <media-play-button commandfor="play-tooltip" class="media-button media-button--subtle media-button--icon media-button--play">
            <media-icon name="restart" class="media-icon media-icon--restart"></media-icon>
            <media-icon name="play" class="media-icon media-icon--play"></media-icon>
            <media-icon name="pause" class="media-icon media-icon--pause"></media-icon>
          </media-play-button>
          <media-tooltip id="play-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>

          <media-seek-button commandfor="seek-backward-tooltip" seconds="-10" class="media-button media-button--subtle media-button--icon media-button--seek">
            <span class="media-icon__container">
              <media-icon name="seek" class="media-icon media-icon--flipped"></media-icon>
              <span class="media-icon__label">10</span>
            </span>
          </media-seek-button>
          <media-tooltip id="seek-backward-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>

          <media-seek-button commandfor="seek-forward-tooltip" seconds="10" class="media-button media-button--subtle media-button--icon media-button--seek">
            <span class="media-icon__container">
              <media-icon name="seek" class="media-icon"></media-icon>
              <span class="media-icon__label">10</span>
            </span>
          </media-seek-button>
          <media-tooltip id="seek-forward-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>
        </div>

        <div class="media-time-controls">
          <media-time type="current" class="media-time"></media-time>
          <media-time-slider class="media-slider">
            <media-slider-track class="media-slider__track">
              <media-slider-fill class="media-slider__fill"></media-slider-fill>
              <media-slider-buffer class="media-slider__buffer"></media-slider-buffer>
            </media-slider-track>
            <media-slider-thumb class="media-slider__thumb"></media-slider-thumb>

            <div class="media-surface media-preview media-slider__preview">
              <media-slider-thumbnail class="media-preview__thumbnail"></media-slider-thumbnail>
              <media-slider-value type="pointer" class="media-time media-preview__time"></media-slider-value>
              <media-icon name="spinner" class="media-preview__spinner media-icon"></media-icon>
            </div>
          </media-time-slider>
          <media-time type="duration" class="media-time"></media-time>
        </div>

        <div class="media-button-group">
          <media-playback-rate-button commandfor="playback-rate-tooltip"  class="media-button media-button--subtle media-button--icon media-button--playback-rate"></media-playback-rate-button>
          <media-tooltip id="playback-rate-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>

          <media-mute-button commandfor="video-volume-popover" class="media-button media-button--subtle media-button--icon media-button--mute">
            <media-icon name="volume-off" class="media-icon media-icon--volume-off"></media-icon>
            <media-icon name="volume-low" class="media-icon media-icon--volume-low"></media-icon>
            <media-icon name="volume-high" class="media-icon media-icon--volume-high"></media-icon>
          </media-mute-button>

          <media-popover id="video-volume-popover" open-on-hover delay="200" close-delay="100" side="top" class="media-surface media-popover media-popover--volume">
            <media-volume-slider class="media-slider" orientation="vertical" thumb-alignment="edge">
              <media-slider-track class="media-slider__track">
                <media-slider-fill class="media-slider__fill"></media-slider-fill>
              </media-slider-track>
              <media-slider-thumb class="media-slider__thumb media-slider__thumb--persistent"></media-slider-thumb>
            </media-volume-slider>
          </media-popover>

          <media-captions-button commandfor="captions-tooltip" class="media-button media-button--subtle media-button--icon media-button--captions">
            <media-icon name="captions-off" class="media-icon media-icon--captions-off"></media-icon>
            <media-icon name="captions-on" class="media-icon media-icon--captions-on"></media-icon>
          </media-captions-button>
          <media-tooltip id="captions-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>

          <media-cast-button commandfor="cast-tooltip" class="media-button media-button--subtle media-button--icon media-button--cast">
            <media-icon name="cast-enter" class="media-icon media-icon--cast-enter"></media-icon>
            <media-icon name="cast-exit" class="media-icon media-icon--cast-exit"></media-icon>
          </media-cast-button>
          <media-tooltip id="cast-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>

          <media-pip-button commandfor="pip-tooltip" class="media-button media-button--subtle media-button--icon media-button--pip">
            <media-icon name="pip-enter" class="media-icon media-icon--pip-enter"></media-icon>
            <media-icon name="pip-exit" class="media-icon media-icon--pip-exit"></media-icon>
          </media-pip-button>
          <media-tooltip id="pip-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>

          <media-fullscreen-button commandfor="fullscreen-tooltip" class="media-button media-button--subtle media-button--icon media-button--fullscreen">
            <media-icon name="fullscreen-enter" class="media-icon media-icon--fullscreen-enter"></media-icon>
            <media-icon name="fullscreen-exit" class="media-icon media-icon--fullscreen-exit"></media-icon>
          </media-fullscreen-button>
          <media-tooltip id="fullscreen-tooltip" side="top" class="media-surface media-tooltip"></media-tooltip>
        </div>
      </media-tooltip-group>
    </media-controls>

    <div class="media-overlay"></div>

    <!-- Hotkeys -->
    <media-hotkey keys="Space" action="togglePaused"></media-hotkey>
    <media-hotkey keys="k" action="togglePaused"></media-hotkey>
    <media-hotkey keys="m" action="toggleMuted"></media-hotkey>
    <media-hotkey keys="f" action="toggleFullscreen"></media-hotkey>
    <media-hotkey keys="c" action="toggleSubtitles"></media-hotkey>
    <media-hotkey keys="i" action="togglePictureInPicture"></media-hotkey>
    <media-hotkey keys="ArrowRight" action="seekStep" value="5"></media-hotkey>
    <media-hotkey keys="ArrowLeft" action="seekStep" value="-5"></media-hotkey>
    <media-hotkey keys="l" action="seekStep" value="10"></media-hotkey>
    <media-hotkey keys="j" action="seekStep" value="-10"></media-hotkey>
    <media-hotkey keys="ArrowUp" action="volumeStep" value="0.05"></media-hotkey>
    <media-hotkey keys="ArrowDown" action="volumeStep" value="-0.05"></media-hotkey>
    <media-hotkey keys="0-9" action="seekToPercent"></media-hotkey>
    <media-hotkey keys="Home" action="seekToPercent" value="0"></media-hotkey>
    <media-hotkey keys="End" action="seekToPercent" value="100"></media-hotkey>
    <media-hotkey keys=">" action="speedUp"></media-hotkey>
    <media-hotkey keys="<" action="speedDown"></media-hotkey>

    <!-- Gestures -->
    <media-gesture type="tap" action="togglePaused" pointer="mouse" region="center"></media-gesture>
    <media-gesture type="tap" action="toggleControls" pointer="touch"></media-gesture>
    <media-gesture type="doubletap" action="seekStep" value="-10" region="left"></media-gesture>
    <media-gesture type="doubletap" action="toggleFullscreen" region="center"></media-gesture>
    <media-gesture type="doubletap" action="seekStep" value="10" region="right"></media-gesture>

    <!-- Input Feedback -->
    <media-status-announcer></media-status-announcer>
    <div class="media-input-feedback">
      <media-volume-indicator hidden class="media-surface media-input-feedback-island media-input-feedback-island--volume">
        <media-volume-indicator-fill class="media-input-feedback-island__content">
          <media-icon name="volume-high" class="media-icon media-icon--volume-high"></media-icon>
          <media-icon name="volume-low" class="media-icon media-icon--volume-low"></media-icon>
          <media-icon name="volume-off" class="media-icon media-icon--volume-off"></media-icon>
          <media-volume-indicator-value class="media-input-feedback-island__value"></media-volume-indicator-value>
        </media-volume-indicator-fill>
      </media-volume-indicator>
      <media-status-indicator
        hidden
        actions="toggleSubtitles toggleFullscreen togglePictureInPicture"
        class="media-surface media-input-feedback-island media-input-feedback-island--status"
      >
        <div class="media-input-feedback-island__content">
          <media-icon name="captions-on" class="media-icon media-icon--captions-on"></media-icon>
          <media-icon name="captions-off" class="media-icon media-icon--captions-off"></media-icon>
          <media-icon name="fullscreen-enter" class="media-icon media-icon--fullscreen-enter"></media-icon>
          <media-icon name="fullscreen-exit" class="media-icon media-icon--fullscreen-exit"></media-icon>
          <media-icon name="pip-enter" class="media-icon media-icon--pip-enter"></media-icon>
          <media-icon name="pip-exit" class="media-icon media-icon--pip-exit"></media-icon>
          <media-status-indicator-value class="media-input-feedback-island__value"></media-status-indicator-value>
        </div>
      </media-status-indicator>
      <media-seek-indicator hidden class="media-input-feedback-bubble">
        <media-icon name="chevron" class="media-icon media-icon--seek"></media-icon>
        <media-seek-indicator-value class="media-time"></media-seek-indicator-value>
      </media-seek-indicator>
      <media-status-indicator hidden actions="togglePaused" class="media-input-feedback-bubble">
        <media-icon name="play" class="media-icon media-icon--play"></media-icon>
        <media-icon name="pause" class="media-icon media-icon--pause"></media-icon>
      </media-status-indicator>
    </div>
  </media-container>
</video-player>
<script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/html/cdn/video-minimal-ui.js"></script>
<link rel="stylesheet" href="./player.css">

<video-player>
  <media-container class="media-minimal-skin media-minimal-skin--video">
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" playsinline></video>

    <media-poster>
      <img src="https://image.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/thumbnail.webp" />
    </media-poster>

    <media-buffering-indicator class="media-buffering-indicator">
      <media-icon name="spinner" family="minimal" class="media-icon"></media-icon>
    </media-buffering-indicator>

    <media-error-dialog class="media-error">
      <div class="media-error__dialog">
        <div class="media-error__content">
          <media-alert-dialog-title class="media-error__title">Something went wrong.</media-alert-dialog-title>
          <media-alert-dialog-description class="media-error__description"></media-alert-dialog-description>
        </div>
        <div class="media-error__actions">
          <media-alert-dialog-close class="media-button media-button--primary">OK</media-alert-dialog-close>
        </div>
      </div>
    </media-error-dialog>

    <media-controls class="media-controls">
      <media-tooltip-group>
        <div class="media-button-group">
          <media-play-button commandfor="play-tooltip" class="media-button media-button--subtle media-button--icon media-button--play">
            <media-icon name="restart" family="minimal" class="media-icon media-icon--restart"></media-icon>
            <media-icon name="play" family="minimal" class="media-icon media-icon--play"></media-icon>
            <media-icon name="pause" family="minimal" class="media-icon media-icon--pause"></media-icon>
          </media-play-button>
          <media-tooltip id="play-tooltip" side="top" class="media-tooltip"></media-tooltip>

          <media-seek-button commandfor="seek-backward-tooltip" seconds="-10" class="media-button media-button--subtle media-button--icon media-button--seek">
            <span class="media-icon__container">
              <media-icon name="seek" family="minimal" class="media-icon media-icon--flipped"></media-icon>
              <span class="media-icon__label">10</span>
            </span>
          </media-seek-button>
          <media-tooltip id="seek-backward-tooltip" side="top" class="media-tooltip"></media-tooltip>

          <media-seek-button commandfor="seek-forward-tooltip" seconds="10" class="media-button media-button--subtle media-button--icon media-button--seek">
            <span class="media-icon__container">
              <media-icon name="seek" family="minimal" class="media-icon"></media-icon>
              <span class="media-icon__label">10</span>
            </span>
          </media-seek-button>
          <media-tooltip id="seek-forward-tooltip" side="top" class="media-tooltip"></media-tooltip>
        </div>

        <div class="media-time-controls">
          <media-time-group class="media-time-group">
            <media-time type="current" class="media-time media-time--current"></media-time>
            <media-time-separator class="media-time-separator"></media-time-separator>
            <media-time type="duration" class="media-time media-time--duration"></media-time>
          </media-time-group>

          <media-time-slider class="media-slider">
            <media-slider-track class="media-slider__track">
              <media-slider-fill class="media-slider__fill"></media-slider-fill>
              <media-slider-buffer class="media-slider__buffer"></media-slider-buffer>
            </media-slider-track>
            <media-slider-thumb class="media-slider__thumb"></media-slider-thumb>

            <div class="media-preview media-slider__preview">
              <div class="media-preview__thumbnail-wrapper">
                <media-slider-thumbnail class="media-preview__thumbnail"></media-slider-thumbnail>
              </div>
              <media-slider-value type="pointer" class="media-time media-preview__time"></media-slider-value>
              <media-icon name="spinner" family="minimal" class="media-preview__spinner media-icon"></media-icon>
            </div>
          </media-time-slider>
        </div>

        <div class="media-button-group">
          <media-playback-rate-button commandfor="playback-rate-tooltip"  class="media-button media-button--subtle media-button--icon media-button--playback-rate"></media-playback-rate-button>
          <media-tooltip id="playback-rate-tooltip" side="top" class="media-tooltip"></media-tooltip>

          <media-mute-button commandfor="video-volume-popover" class="media-button media-button--subtle media-button--icon media-button--mute">
            <media-icon name="volume-off" family="minimal" class="media-icon media-icon--volume-off"></media-icon>
            <media-icon name="volume-low" family="minimal" class="media-icon media-icon--volume-low"></media-icon>
            <media-icon name="volume-high" family="minimal" class="media-icon media-icon--volume-high"></media-icon>
          </media-mute-button>

          <media-popover id="video-volume-popover" open-on-hover delay="200" close-delay="100" side="top" class="media-popover media-popover--volume">
            <media-volume-slider class="media-slider" orientation="vertical" thumb-alignment="edge">
              <media-slider-track class="media-slider__track">
                <media-slider-fill class="media-slider__fill"></media-slider-fill>
              </media-slider-track>
              <media-slider-thumb class="media-slider__thumb media-slider__thumb--persistent"></media-slider-thumb>
            </media-volume-slider>
          </media-popover>

          <media-captions-button commandfor="captions-tooltip" class="media-button media-button--subtle media-button--icon media-button--captions">
            <media-icon name="captions-off" family="minimal" class="media-icon media-icon--captions-off"></media-icon>
            <media-icon name="captions-on" family="minimal" class="media-icon media-icon--captions-on"></media-icon>
          </media-captions-button>
          <media-tooltip id="captions-tooltip" side="top" class="media-tooltip"></media-tooltip>

          <media-cast-button commandfor="cast-tooltip" class="media-button media-button--subtle media-button--icon media-button--cast">
            <media-icon name="cast-enter" family="minimal" class="media-icon media-icon--cast-enter"></media-icon>
            <media-icon name="cast-exit" family="minimal" class="media-icon media-icon--cast-exit"></media-icon>
          </media-cast-button>
          <media-tooltip id="cast-tooltip" side="top" class="media-tooltip"></media-tooltip>

          <media-pip-button commandfor="pip-tooltip" class="media-button media-button--subtle media-button--icon media-button--pip">
            <media-icon name="pip-enter" family="minimal" class="media-icon media-icon--pip-enter"></media-icon>
            <media-icon name="pip-exit" family="minimal" class="media-icon media-icon--pip-exit"></media-icon>
          </media-pip-button>
          <media-tooltip id="pip-tooltip" side="top" class="media-tooltip"></media-tooltip>

          <media-fullscreen-button commandfor="fullscreen-tooltip" class="media-button media-button--subtle media-button--icon media-button--fullscreen">
            <media-icon name="fullscreen-enter" family="minimal" class="media-icon media-icon--fullscreen-enter"></media-icon>
            <media-icon name="fullscreen-exit" family="minimal" class="media-icon media-icon--fullscreen-exit"></media-icon>
          </media-fullscreen-button>
          <media-tooltip id="fullscreen-tooltip" side="top" class="media-tooltip"></media-tooltip>
        </div>
      </media-tooltip-group>
    </media-controls>

    <div class="media-overlay"></div>

    <!-- Hotkeys -->
    <media-hotkey keys="Space" action="togglePaused"></media-hotkey>
    <media-hotkey keys="k" action="togglePaused"></media-hotkey>
    <media-hotkey keys="m" action="toggleMuted"></media-hotkey>
    <media-hotkey keys="f" action="toggleFullscreen"></media-hotkey>
    <media-hotkey keys="c" action="toggleSubtitles"></media-hotkey>
    <media-hotkey keys="i" action="togglePictureInPicture"></media-hotkey>
    <media-hotkey keys="ArrowRight" action="seekStep" value="5"></media-hotkey>
    <media-hotkey keys="ArrowLeft" action="seekStep" value="-5"></media-hotkey>
    <media-hotkey keys="l" action="seekStep" value="10"></media-hotkey>
    <media-hotkey keys="j" action="seekStep" value="-10"></media-hotkey>
    <media-hotkey keys="ArrowUp" action="volumeStep" value="0.05"></media-hotkey>
    <media-hotkey keys="ArrowDown" action="volumeStep" value="-0.05"></media-hotkey>
    <media-hotkey keys="0-9" action="seekToPercent"></media-hotkey>
    <media-hotkey keys="Home" action="seekToPercent" value="0"></media-hotkey>
    <media-hotkey keys="End" action="seekToPercent" value="100"></media-hotkey>
    <media-hotkey keys=">" action="speedUp"></media-hotkey>
    <media-hotkey keys="<" action="speedDown"></media-hotkey>

    <!-- Gestures -->
    <media-gesture type="tap" action="togglePaused" pointer="mouse" region="center"></media-gesture>
    <media-gesture type="tap" action="toggleControls" pointer="touch"></media-gesture>
    <media-gesture type="doubletap" action="seekStep" value="-10" region="left"></media-gesture>
    <media-gesture type="doubletap" action="toggleFullscreen" region="center"></media-gesture>
    <media-gesture type="doubletap" action="seekStep" value="10" region="right"></media-gesture>

    <!-- Input Feedback -->
    <media-status-announcer></media-status-announcer>
    <div class="media-input-feedback">
      <media-volume-indicator hidden class="media-input-feedback-island media-input-feedback-island--volume">
        <media-volume-indicator-fill class="media-input-feedback-island__content">
          <media-icon name="volume-high" family="minimal" class="media-icon media-icon--volume-high"></media-icon>
          <media-icon name="volume-low" family="minimal" class="media-icon media-icon--volume-low"></media-icon>
          <media-icon name="volume-off" family="minimal" class="media-icon media-icon--volume-off"></media-icon>
          <div class="media-input-feedback-island__progress" aria-hidden="true"></div>
          <media-volume-indicator-value class="media-input-feedback-island__value"></media-volume-indicator-value>
        </media-volume-indicator-fill>
      </media-volume-indicator>

      <media-status-indicator hidden actions="toggleSubtitles toggleFullscreen togglePictureInPicture" class="media-input-feedback-island media-input-feedback-island--status">
        <div class="media-input-feedback-island__content">
          <media-icon name="captions-on" family="minimal" class="media-icon media-icon--captions-on"></media-icon>
          <media-icon name="captions-off" family="minimal" class="media-icon media-icon--captions-off"></media-icon>
          <media-icon name="fullscreen-enter" family="minimal" class="media-icon media-icon--fullscreen-enter"></media-icon>
          <media-icon name="fullscreen-exit" family="minimal" class="media-icon media-icon--fullscreen-exit"></media-icon>
          <media-icon name="pip-enter" family="minimal" class="media-icon media-icon--pip-enter"></media-icon>
          <media-icon name="pip-exit" family="minimal" class="media-icon media-icon--pip-exit"></media-icon>
          <media-status-indicator-value class="media-input-feedback-island__value"></media-status-indicator-value>
        </div>
      </media-status-indicator>

      <media-seek-indicator hidden class="media-input-feedback-bubble">
        <media-icon name="chevron" family="minimal" class="media-icon media-icon--seek"></media-icon>
        <media-seek-indicator-value class="media-time"></media-seek-indicator-value>
      </media-seek-indicator>

      <media-status-indicator hidden actions="togglePaused" class="media-input-feedback-bubble">
        <media-icon name="play" family="minimal" class="media-icon media-icon--play"></media-icon>
        <media-icon name="pause" family="minimal" class="media-icon media-icon--pause"></media-icon>
      </media-status-indicator>
    </div>
  </media-container>
</video-player>

CSP

If your application uses a Content Security Policy, you may need to allow additional sources for player features to work correctly.

Common requirements

  • media-src must allow your media URLs.
  • img-src must allow any poster or thumbnail image URLs.
  • connect-src must allow HLS manifests, playlists, captions, and segment requests when using HLS playback.
  • media-src blob: is required when using the HLS player variants, which use MSE-backed playback.
  • worker-src blob: is required when using the hls.js player variants.
  • style-src 'unsafe-inline' is currently required for some player UI and HTML player styling behavior.

Example

Content-Security-Policy:
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' https: data: blob:;
  media-src 'self' https: blob:;
  connect-src 'self' https:;
  worker-src 'self' blob:;

See also


That’s it! You now have a fully functional Video.js player. Go forth and play.

Something not quite right? You can submit an issue and ask for help, or explore other support options.