Video
The Video component provides a video player that supports both self-hosted videos (MP4, HLS, DASH)
and YouTube videos. It's a compound component: a source-agnostic Video.Root wraps a single player
subcomponent that determines where the video comes from.

There are two players available:
Video.Player— plays self-hosted videos and offers more customization (custom controls, poster, lightbox).Video.YouTube— embeds a YouTube video through YouTube's official IFrame API, with less customization to stay compliant with YouTube's Terms of Service.
Video.Root owns the shared playback options (autoplay, muted, loop) and the playback events
(onPlay, onPause, onEnded, onProgress, onBuffer), so those behave the same regardless of the
video source.
Lazy loading
Usage
Compose a Video.Root with a single player as its child.
import type { NubeSDK } from "@tiendanube/nube-sdk-types";
import { Video } from "@tiendanube/nube-sdk-jsx";
function MyComponent() {
return (
<Video.Root width="100%">
<Video.Player
src="https://cdn.example.com/product-demo.mp4"
poster="https://cdn.example.com/poster.jpg"
controls
/>
</Video.Root>
);
}
export function App(nube: NubeSDK) {
nube.render("after_product_description", <MyComponent />);
}
To embed a YouTube video, swap Video.Player for Video.YouTube. The src accepts either a YouTube
video ID or any youtube.com / youtu.be URL:
import type { NubeSDK } from "@tiendanube/nube-sdk-types";
import { Video } from "@tiendanube/nube-sdk-jsx";
function MyComponent() {
return (
<Video.Root width="100%">
<Video.YouTube
src="https://www.youtube.com/watch?v=dlZf-eTkU7k"
controls
/>
</Video.Root>
);
}
export function App(nube: NubeSDK) {
nube.render("after_product_description", <MyComponent />);
}
Source URLs
Playback options
autoplay, muted and loop are set on Video.Root and apply to whichever player is used.
<Video.Root autoplay loop>
<Video.Player src="https://cdn.example.com/background-loop.mp4" controls={false} />
</Video.Root>
Autoplay is always muted
Lightbox (self-hosted only)
Set lightbox on Video.Player to replace the native fullscreen button with a custom fullscreen overlay.
This gives a consistent fullscreen experience across browsers and, unlike native fullscreen, keeps playback
state (current time, buffered range) when opening and closing. It's not available on Video.YouTube, which
uses YouTube's own native fullscreen.
<Video.Root>
<Video.Player src="https://cdn.example.com/product-demo.mp4" lightbox />
</Video.Root>
Any children placed after the player inside Video.Root are rendered as an overlay on top of the video
while the lightbox is open — useful for captions, calls to action, or other content layered over the video.
<Video.Root>
<Video.Player src="https://cdn.example.com/product-demo.mp4" lightbox />
<Text modifiers={["bold"]}>Shop the look</Text>
</Video.Root>
Fullscreen
Rendering a Video.Root into the fullscreen_content slot
plays it edge to edge over the page, inside a dialog with an opaque backdrop and a close button in the
top-right corner. There is no fullscreen prop: the video adapts because of where it is rendered.
import type { NubeSDK } from "@tiendanube/nube-sdk-types";
import { Video } from "@tiendanube/nube-sdk-jsx";
export function App(nube: NubeSDK) {
nube.render("fullscreen_content", (
<Video.Root
autoplay
onClose={() => nube.clearSlot("fullscreen_content")}
>
<Video.Player src="https://cdn.example.com/product-demo.mp4" />
</Video.Root>
));
}
Inside the overlay Video.Player behaves differently from its inline presentation:
- It fills the stage and is letterboxed to the video's own aspect ratio, so a vertical clip keeps its shape
instead of being cropped to the inline box.
aspectRatioonly applies to the inline presentation and has no effect here. - The fullscreen and
lightboxbuttons are dropped — the overlay already is that presentation, and closing it is the host's close button or the Esc key. - Controls always use the desktop layout, in a single bottom bar, so the mute button never collides with the close button.
- Overlay children are always visible on top of the video, not only while a lightbox is open.
Use Video.Player for fullscreen
Only Video.Root is admitted
The slot holds one video at a time. Clearing it with nube.clearSlot("fullscreen_content") closes the overlay,
which is the usual way to react to onClose.
Events
The playback lifecycle events live on Video.Root because they are the same for every video source.
Each handler receives an object with type, the current SDK state, and a value payload:
| Event | value payload | Description |
|---|---|---|
onPlay | { currentTime: number } | Fired when playback starts or resumes. |
onPause | { currentTime: number } | Fired when playback is paused. |
onEnded | { currentTime: number } | Fired when the video reaches the end. |
onProgress | { percent: number, currentTime: number, duration: number } | Fired periodically (about once per second) during playback. |
onBuffer | { buffering: boolean } | Fired when buffering starts (true) or stops (false). |
<Video.Root
onPlay={({ value }) => console.log("playing at", value.currentTime)}
onPause={({ value }) => console.log("paused at", value.currentTime)}
onEnded={() => console.log("finished")}
onProgress={({ value }) => console.log(`${value.percent}%`)}
onBuffer={({ value }) => console.log("buffering:", value.buffering)}
>
<Video.Player src="https://cdn.example.com/product-demo.mp4" controls />
</Video.Root>
Fullscreen lifecycle
onOpen and onClose are not playback events: they report the fullscreen overlay opening and
closing. Both carry an empty value — there is no currentTime to read — so use them as signals rather
than for playback position. They do not fire for the lightbox.
| Event | value payload | Description |
|---|---|---|
onOpen | {} | Fired when the video mounts inside the fullscreen overlay. |
onClose | {} | Fired when the overlay closes (close button, Esc, or clearing the slot). |
Errors
Errors are source-specific, so onError lives on the player rather than on Video.Root. The handler
receives a value payload with a source, a code, and a human-readable message.
Video.Player reports source: "video-player" with one of these codes:
| Code | Description |
|---|---|
invalid_source | The src URL is missing, unsafe, or unsupported. |
media_err_aborted | Playback was aborted. |
media_err_network | A network error interrupted the download. |
media_err_decode | The media could not be decoded. |
media_err_src_not_supported | The source format is not supported. |
playback_error | A playback failure with no more specific code. |
Video.YouTube reports source: "video-youtube" with one of these codes:
| Code | Description |
|---|---|
invalid_source | The src is not a recognizable YouTube video ID or URL. |
invalid_video_id | YouTube rejected the video ID. |
html5_error | The video can't be played in an HTML5 player. |
video_not_found | The video was not found or was removed. |
embedding_not_allowed | The video owner disabled embedding. |
playback_error | A playback failure with no more specific code. |
<Video.Root>
<Video.Player
src="https://cdn.example.com/product-demo.mp4"
onError={({ value }) => console.error(value.code, value.message)}
/>
</Video.Root>
Subcomponents
Video.Root
The source-agnostic wrapper. It controls layout and the shared playback options and events, and must contain
exactly one player (Video.Player or Video.YouTube), optionally followed by overlay children.
| Property | Type | Required | Description |
|---|---|---|---|
| children | NubeChildrenComponent | Yes | A single Video.Player or Video.YouTube, optionally followed by overlay content. |
| width | Size | No | Width of the video (e.g., "100%", "600px", 600). Defaults to "100%". |
| height | Size | No | Height of the video. |
| autoplay | boolean | No | Starts playback on mount. Always muted when true. |
| muted | boolean | No | Starts the video muted. |
| loop | boolean | No | Restarts the video automatically when it ends. |
| onPlay | (event) => void | No | Called when playback starts or resumes. |
| onPause | (event) => void | No | Called when playback is paused. |
| onEnded | (event) => void | No | Called when the video reaches the end. |
| onProgress | (event) => void | No | Called periodically during playback. |
| onBuffer | (event) => void | No | Called when buffering starts or stops. |
| onOpen | (event) => void | No | Called when the video opens in the fullscreen overlay. |
| onClose | (event) => void | No | Called when the fullscreen overlay closes. |
| style | StyleSheet | No | Custom styles for the video container. |
| id | string | No | Optional unique identifier for the component. |
Video.Player
Plays a self-hosted video (MP4, HLS or DASH) and must be a child of Video.Root.
| Property | Type | Required | Description |
|---|---|---|---|
| src | string | Yes | Absolute http(s) URL of an MP4, HLS (.m3u8) or DASH (.mpd) video. |
| poster | string | No | Absolute http(s) URL of a poster image shown before playback. |
| controls | boolean | No | Show the player's default controls. Defaults to true. |
| aspectRatio | string | No | Aspect ratio as "width/height" (e.g. "16/9", "9/16"). Defaults to "16/9". Ignored in fullscreen . |
| lightbox | boolean | No | Replace the native fullscreen button with a custom fullscreen overlay. |
| onError | (event) => void | No | Called on a source-specific playback error (see Errors ). |
| id | string | No | Optional unique identifier for the component. |
Video.YouTube
Embeds a YouTube video through YouTube's official IFrame API and must be a child of Video.Root.
| Property | Type | Required | Description |
|---|---|---|---|
| src | string | Yes | A YouTube video ID (e.g. YE7VzlLtp-4) or any youtube.com / youtu.be URL. |
| controls | boolean | No | Show YouTube's native control bar. Defaults to false. |
| allowFullscreen | boolean | No | Allow fullscreen via YouTube's native fullscreen button. Defaults to true. Only reachable when controls is true. |
| onError | (event) => void | No | Called on a source-specific playback error (see Errors ). |
| id | string | No | Optional unique identifier for the component. |
YouTube branding
Help us improve NubeSDK
Found an issue or have a suggestion? Let us know on GitHub.