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, registers the fullscreen and activity target, and gives gesture and hotkey components a shared interaction surface. It lives inside a Player.
The <media-container> is the player’s physical surface. It defines the visual boundary, registers the fullscreen and activity target, and gives gesture and hotkey elements a shared interaction surface. It lives inside a <video-player>.
<Player>
<Container>
<Video src="video.mp4" />
<Controls.Root>
<Controls.Content>{/* ... */}</Controls.Content>
</Controls.Root>
</Container>
</Player><video-player>
<media-container>
<video src="video.mp4"></video>
<media-controls><media-controls-content>...</media-controls-content></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 { Video, videoFeatures } from '@videojs/react/video';
const { Player } = createPlayer({ features: videoFeatures });Import the standard player entry point. It registers both <video-player> and <media-container>:
import '@videojs/html/video/player';<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);Extending ContainerElement automatically registers the element with the nearest player when it connects and releases that registration when it disconnects.
Import @videojs/html/ui/container when you only need to register the standard <media-container> element.
What it does
Layout and fullscreen
The container is the visual box around your media and controls. Put sizing, aspect ratio, positioning, and visual boundaries here — on Container, not Player. Place overlays inside it, use it as their positioning context, and measure it when your app needs the rendered player size.
<Container style={{ position: 'relative', width: 640, aspectRatio: '16/9' }}>
<Video src="video.mp4" />
<Controls.Root>
<Controls.Content>{/* ... */}</Controls.Content>
</Controls.Root>
</Container>.player-surface {
position: relative;
display: block;
width: 640px;
aspect-ratio: 16 / 9;
}<media-container class="player-surface">
<video src="video.mp4"></video>
<media-controls><media-controls-content>...</media-controls-content></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.
Custom video elements such as <mux-video> and <hlsjs-video> do not create their own layout boxes: their inner <video> fills the parent. Size and measure <media-container>, not <mux-video> or <hlsjs-video>. Native <video> and <audio> elements still render their own boxes.
React media components such as MuxVideo render a native media element, so the media itself has a box. The container remains the shared layout and fullscreen target for the media and its overlays.
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 — Tap and double-tap actions, optionally limited by pointer type and left, center, or right region. 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.
Popup coordination
Each container owns one popup group. Opening a root menu or popover closes the previously open popup in that container. A popup rendered outside the container still manages its own open state, but it does not participate in the container’s group.
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.
Style VideoSkin for the player’s outer size and aspect ratio. Its skin.css import makes the built-in container fill that space and positions the media, controls, poster, and overlays.
If you omit VideoSkin to build custom UI, render and style Container yourself. See Layout and fullscreen for the complete layout example.
Style <video-skin> for the player’s outer size and aspect ratio. Importing the skin registers the styles that make the built-in container fill that space and position the media, controls, poster, and overlays.
If you omit <video-skin> to build custom UI, render and style <media-container> yourself. See Layout and fullscreen for the complete layout example.
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.Content>{/* ... */}</Controls.Content>
</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-content>...</media-controls-content></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.