Skip to main content
New in version 10: In prior versions of Video.js, skins were CSS-only themes applied to the same default controls. In version 10, skins include the UI components themselves — each skin can be completely unique and include only the components it needs.
A skin wraps your media element and provides the full player UI:
<Player.Provider>
  <VideoSkin>
    {/* wraps the Media component */}
    <Video src="video.mp4" />
  </VideoSkin>
</Player.Provider>

Packaged vs. ejected

When you use a skin, you have two options: packaged or ejected. It’s usually easiest to start packaged and eject later when you need more customization.
PackagedEjected
ComponentsSingle componentMany UI components
CustomizationLimitedComplete
Design updatesAuto-applied on version bumpApplied manually, or intentionally ignored

Packaged

<Player.Provider>
  <VideoSkin>
    {/* ...Media... */}
  </VideoSkin>
</Player.Provider>

Ejected

When you eject a skin, you get the individual components and their markup. You can then customize each piece independently.
<Player.Provider>
  <Container className={cn('media-minimal-skin', className)} {...rest}>
    <BufferingIndicator
      render={(props) => (
        <div {...props} className="media-buffering-indicator">
          <SpinnerIcon className="media-icon" />
        </div>
      )}
    />

    <Controls.Root className="media-controls">
      <PlayButton
        render={(props) => (
          <Button {...props} className="media-button--icon media-button--play">
            <PlayIcon className="media-icon media-icon--play" />
            <PauseIcon className="media-icon media-icon--pause" />
          </Button>
        )}
      />

      <TimeSlider.Root className="media-slider">
        <TimeSlider.Track className="media-slider__track">
          <TimeSlider.Fill className="media-slider__fill" />
          <TimeSlider.Buffer className="media-slider__buffer" />
        </TimeSlider.Track>
        <TimeSlider.Thumb className="media-slider__thumb" />
      </TimeSlider.Root>

      <MuteButton
        render={(props) => (
          <Button {...props} className="media-button--icon media-button--mute">
            <VolumeOffIcon className="media-icon media-icon--volume-off" />
            <VolumeHighIcon className="media-icon media-icon--volume-high" />
          </Button>
        )}
      />

      <FullscreenButton
        render={(props) => (
          <Button {...props} className="media-button--icon media-button--fullscreen">
            <FullscreenEnterIcon className="media-icon media-icon--fullscreen-enter" />
            <FullscreenExitIcon className="media-icon media-icon--fullscreen-exit" />
          </Button>
        )}
      />
    </Controls.Root>
  </Container>
</Player.Provider>
See UI components for the full list of building blocks.

Skins, features, and presets

Each skin is built with specific features in mind. A video skin renders fullscreen and picture-in-picture controls. An audio skin doesn’t. The features associated with a skin are called a feature bundle.
Feature bundleAvailable skinsImport
videoFeatures<VideoSkin>, <MinimalVideoSkin>@videojs/react/video
audioFeatures<AudioSkin>, <MinimalAudioSkin>@videojs/react/audio
backgroundFeatures<BackgroundVideoSkin>@videojs/react/background
See Presets to learn more about skins and feature bundles.

Build docs developers (and LLMs) love