Container
The player's visual and interaction surface for layout, fullscreen, focus, and user activity.
The Container is the player’s physical surface. It defines the visual boundary, attaches the media element, and detects user interaction like activity and (eventually) gestures and keyboard input. It lives inside a Player.
The <media-container> is the player’s physical surface. It defines the visual boundary, attaches the media element, and detects user interaction like activity and (eventually) gestures and keyboard input. It lives inside a <video-player>.
<Player>
<Container>
<video src="video.mp4" />
<Controls.Root>
{/* ... */}
</Controls.Root>
</Container>
</Player><video-player>
<media-container>
<video src="video.mp4"></video>
<media-controls>...</media-controls>
</media-container>
</video-player>How it’s created
Import Container from the main React package. It connects to whichever Player contains it, so it does not need to be created for a specific feature set.
import { Container, createPlayer } from '@videojs/react';
import { videoFeatures } from '@videojs/react/video';
const { Player } = createPlayer({ features: videoFeatures });Register the standard player and skin entry points for a complete video player. The skin supplies <media-container> and the UI elements it uses:
import '@videojs/html/video/player';
import '@videojs/html/video/skin';<video-player>
<media-container>
<video src="video.mp4"></video>
</media-container>
</video-player>For custom behavior, extend the same ContainerElement used by the built-in player:
import { ContainerElement } from '@videojs/html';
class MyContainer extends ContainerElement {}
customElements.define('my-container', MyContainer);Import @videojs/html/ui/container when you only need to register the standard <media-container> element.
What it does
Layout & fullscreen
The container is the visual box around your media and controls. Sizing, aspect ratio, and visual boundaries all go here — on Container, not Player.
<Container style={{ width: 640, aspectRatio: '16/9' }}>
<video src="video.mp4" />
<Controls.Root>
{/* ... */}
</Controls.Root>
</Container><media-container style="width: 640px; aspect-ratio: 16/9;">
<video src="video.mp4"></video>
<media-controls>...</media-controls>
</media-container>When the user goes fullscreen, the container goes fullscreen — not the video element. This keeps controls and other UI visible on top of the video, since they’re children of the container.
Media attachment
Media discovery is handled by Player, not Container. When a media component like <Video> registers itself via context, Player wires it to the store and all of the player’s features.
Media discovery is handled by the player provider, not the container. Custom media elements like <hlsjs-video> register themselves via context when they connect. Plain <video> and <audio> elements are tracked automatically, including when they are added, removed, or replaced after connection. No slot="media" attribute is needed.
Interaction surface
The container is where user intent enters the player. It listens for physical interaction on its surface and translates that into player behavior:
- User activity — Mouse movement, touch, and keyboard activity within the container drive idle detection. This is how controls know when to show and hide.
- Gestures — Click-to-play, double-click fullscreen, swipe to seek, and other touch/mouse gestures. Configured via the
Gesturecomponent. - Keyboard controls — Spacebar to play/pause, arrow keys to seek, and other keyboard shortcuts scoped to the container. Configured via the
Hotkeycomponent.
Relationship to skins
A skin is a container plus UI controls. When you use a packaged skin, the container is built in — you don’t need to add one yourself.
{/* Packaged skin — container is inside VideoSkin */}
<Player>
<VideoSkin>
<Video src="video.mp4" />
</VideoSkin>
</Player>
{/* Custom UI — you use Container directly */}
<Player>
<Container>
<video src="video.mp4" />
<PlayButton />
</Container>
</Player><!-- Packaged skin — container is inside video-skin -->
<video-player>
<video-skin>
<video src="video.mp4"></video>
</video-skin>
</video-player>
<!-- Custom UI — you use media-container directly -->
<video-player>
<media-container>
<video src="video.mp4"></video>
<media-play-button></media-play-button>
</media-container>
</video-player>Inside vs. outside the container
Player gives components access to state and actions. Container layers on physical behaviors — fullscreen, activity detection, and gesture handling. Components work in both places; the container just adds those extras.
<Player>
<Container>
<video src="video.mp4" />
<Controls.Root> {/* fullscreen, activity detection, gestures */}
{/* ... */}
</Controls.Root>
</Container>
<Transcript /> {/* state & actions, but no container behaviors */}
<PlaylistSidebar /> {/* state & actions, but no container behaviors */}
</Player><video-player>
<media-container>
<video src="video.mp4"></video>
<media-controls>...</media-controls> <!-- fullscreen, activity detection, gestures -->
</media-container>
<media-transcript></media-transcript> <!-- state & actions, but no container behaviors -->
<playlist-sidebar></playlist-sidebar> <!-- state & actions, but no container behaviors -->
</video-player>A play button outside the container still reads playback state and can toggle play/pause — it just won’t go fullscreen with the player or respond to the container’s idle state.