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>
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>
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. |
| 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. |
| 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.