Video Player
VideoPlayer— the poster hero.playbackModeopens the video in a modal, swaps it inline into the card, or renders it directly on load;sourceputs either a YouTube embed or a native HTML5<video>behind it. No player library.PlyrPlayer— Plyr driving HTML5 files, YouTube and Vimeo behind one skinnable control bar.JwPlayer— JW Player with colour skin presets, for teams already on JW's platform.
YouTube & native video
modal mode Esc closes the dialog and focus returns to the play button.Modal player
The default. A full-width 4K nature preview, glass CTA button and a centered dialog player.
playbackMode"modal"- opens the video in a centered dialog.
Esccloses it, the page behind it stops scrolling, and focus returns to the play button. thumbnailUrl- is the preview background and
videoUrlthe embed that only loads once the modal opens. modalSize- sizes the dialog — md | lg | xl | full.
fullfills the viewport at 90vh.
Inline player
Same poster hero, but the player swaps into the card instead of opening an overlay.
playbackMode"inline"- replaces the poster with the player in place. Nothing covers the page and body scrolling is untouched, so it is safe inside a scrolling column or a carousel slide.
- The close button restores the poster — use it over
playbackMode"modal" - when the hero sits mid-page rather than above the fold.
Direct embed
No poster at all — the embed renders on load and keeps its own title chrome.
playbackMode"direct"- drops the thumbnail and the CTA, so the embed is the whole component.
- No copy is drawn over an embed:
source"youtube" - already renders YouTube's own title bar, and a second one stacked on it is duplicate chrome.
titlestill reaches the iframe as its accessible name. The native player below gets the full treatment. autoPlay- is only read in this mode — the other two are click-initiated and always start playing. The native player mutes itself when it is set, because browsers block unmuted autoplay.
Native HTML5 video
A self-hosted mp4 with the browser's own controls — no embed, no third-party player.
source"video"- renders a real
<video>element instead of an iframe, sovideoUrlis a media file andthumbnailUrlbecomes itsposter. videoType"video/mp4"- goes on the
<source>so the browser can skip a codec it cannot play.loop,mutedandcontrolspass straight through. - The title sits at the foot of the frame until you press play, then moves to a hover-only top bar the way YouTube does. Set
showOverlay{false} - for a bare player with no copy on it at all.
Native video in a modal
The same file behind the poster hero, opening ultrawide at full viewport size.
source"video"- works in every playback mode — the reveal and the player are independent choices.
onPlay- and
onClosefire on open and dismiss, which is where analytics goes.
Install the video player
New project? Follow the installation guide to set up Tailwind CSS, shadcn, the cn helper and lucide-react, register the @algorize-ui registry once, then copy your styles from the Themes page.
Install Video Player
components/ is written into your project. Copy any snippet from a Usage tab above and it runs.
Install dependencies
Nothing beyond the shared setup — Video Player needs only the cn helper and lucide-react, which the installation guide already put in place.
Plyr player
plyr package and is imported dynamically inside an effect, which keeps the component server-renderable.Plyr — HTML5
A self-hosted mp4 behind Plyr's control bar, skinned with a single CSS variable.
provider"html5"- plays
srcas a media file.srcTypegoes on the<source>andposterUrlon the<video>. accentColor- sets Plyr's
--plyr-color-main, which is what actually colours the control bar, the progress fill and the focus ring.
Plyr — YouTube
A YouTube embed wearing exactly the same controls as the file above.
provider"youtube"- accepts a watch URL or a bare video id —
srcis normalised to the embed id either way.vimeoworks the same. controls- takes Plyr's control-name array in bar order, so you can drop settings, captions or the volume slider.
Install the Plyr player
Install Video Player (Plyr)
components/ is written into your project. Copy any snippet from a Usage tab above and it runs.
Install dependencies
Step 1 installs this for you. Run it yourself only if you are adding Video Player (Plyr) to an existing project by hand. The cn helper and lucide-react are not listed — the installation guide covers them once for every component.
plyr is an npm package.
JW Player
JwPlayer loads it from a libraryUrl you supply and falls back to a native <video> if it is unavailable. The previews below run on JW's public documentation library — see the setup note under Installation before shipping.JW Player — default skin
JW's own control bar, untouched, around a JW-hosted playlist.
libraryUrl- is the JW library script. It defaults to JW's public docs library so the component works out of the box — replace it with your own player id before shipping.
playlistUrl- points at a JW playlist or media endpoint; use
fileinstead for a plain mp4 or HLS manifest.
JW Player — skinned
The same player with a colour preset applied to the control bar, menus, time slider and tooltips.
skin"midnight"- picks a pre-mixed colour config — default | midnight | neon | sunset | minimal.
skinConfig- takes the raw JW skin object and wins over
skin;skinUrlandskinNamepoint at a custom stylesheet instead.
JW Player — custom palette
A transparent control bar over your own media, with extra JW options merged into setup.
skin"minimal"- removes the control bar background so it sits directly on the frame.
playerOptions- is merged last into
jwplayer().setup()— the escape hatch for anything the props do not cover.
Install the JW player
Install Video Player (JW Player)
components/ is written into your project. Copy any snippet from a Usage tab above and it runs.
Install dependencies
Nothing beyond the shared setup — Video Player (JW Player) needs only the cn helper and lucide-react, which the installation guide already put in place.
JW Player setup
JwPlayer has no npm dependency: JW ships a licensed library from its own CDN, and the component loads it at runtime.- Create a player in the JW Dashboard and copy its cloud-hosted library URL —
https://cdn.jwplayer.com/libraries/<PLAYER_ID>.js. - Pass it as
libraryUrl. For a self-hosted build, also pass thelicenseKeyfrom your player settings. - Leave
libraryUrlunset only while evaluating. The default is JW's public documentation library, which carries JW's branding and can be rotated or rate-limited without notice.
<video> with the poster and a visible warning rather than a black box — so a licence problem degrades instead of breaking the page.Features
Three Reveal Modes
Open in a dialog, swap the player into the card in place, or render it directly on load — one prop, no layout rewrite.
Four Player Backends
YouTube embeds, native HTML5 video, Plyr and JW Player — sharing one hero card, one title treatment and one glow treatment.
Deferred Media Weight
In modal and inline mode nothing but the poster loads until a click, so an embed never lands on your Largest Contentful Paint.
Responsive Aspect Ratios
16:9, ultrawide 21:9, classic 4:3 and square 1:1, forwarded to Plyr and JW in their own ratio formats so the frame never jumps.
Keyboard Accessible
The play CTA is a real button, the modal is a labelled dialog, Esc closes it, the page behind it stops scrolling, and focus returns to the trigger.
Themed Accent
Play buttons and progress fills read --theme-color, falling back to your --primary, so the players match the rest of the page.
Best Practices & Guidelines
Picking a playback mode
Use modal above the fold, where the video is the page's main call to action. Use inline mid-page — an overlay that hijacks the whole viewport from a card the reader scrolled past reads as an interruption. Use direct when the video is the content, such as a documentation walkthrough.
Autoplay is a request, not a guarantee
Browsers block autoplay with sound. autoPlay only applies to playbackMode="direct", and the native player mutes itself when it is set. In modal and inline the click is the gesture, so playback starts on its own and the component adds autoplay=1 to the embed URL for you.
Optimizing thumbnails
Give thumbnailUrl a resolution that matches your aspectRatio. It is the poster, the LCP candidate and — for source="video" — the <video> element's own poster, so a mismatched crop shows up in all three places.
The title treatment follows playback
Before anything has played, the title and subtitle sit at the foot of the frame and stay there — nothing is happening yet, so they are free to introduce the video, and nobody should have to hover to learn what they are about to watch. Once playback starts the picture is the point, so the copy moves to a top bar that only appears on hover, the way YouTube does. It never returns to the bottom, because pausing does not undo having started. This applies to the players we draw the chrome for — the native <video>, Plyr and JW. A YouTube or Vimeo embed already paints its own title bar, so nothing is layered on top of it.
Choosing a backend
Start with VideoPlayer — it ships no player library. Move to Plyr when you need one consistent, skinnable control bar across YouTube, Vimeo and self-hosted files. Move to JW Player when you are already on JW's platform for adaptive streaming, DRM or analytics.
Props Reference
VideoPlayer
| Prop | Type | Default | Description |
|---|---|---|---|
videoUrl | string | "https://www.youtube..." | Embed URL for source="youtube", or a playable media file for source="video". |
playbackMode | "modal" | "inline" | "direct" | "modal" | How the player is revealed: a centered dialog, an in-place swap inside the card, or immediately on load with the title laid over the frame. |
source | "youtube" | "video" | "youtube" | Renders an iframe embed or a native <video> element. Independent of playbackMode. |
videoType | string | undefined | MIME type on the <source> for source="video", e.g. video/mp4. |
thumbnailUrl | string | "https://images.unsplash..." | Poster image for the card, and the poster attribute of the native player. |
thumbnailAlt | string | "Nature Wilderness..." | Alt text for the poster image. |
badge | string | "Wilderness Exploration" | Pill badge with an animated icon, top-left over the poster. Poster hero only — direct mode shows just the title and subtitle. |
title | string | "Experience Untouched..." | Headline, and the accessible name of the modal dialog. |
subtitle | string | "Embark on an immersive..." | Secondary paragraph under the title. |
playButtonLabel | string | "Watch Nature Documentary" | Label inside the glassmorphic CTA. Not rendered in direct mode. |
aspectRatio | "16/9" | "21/9" | "4/3" | "1/1" | "16/9" | Aspect ratio of the card and of the modal frame. |
modalSize | "md" | "lg" | "xl" | "full" | "lg" | Width of the dialog. full fills the viewport at 90vh. Only read in modal mode. |
autoPlay | boolean | false | Starts playback on load. Only read in direct mode; the native player mutes itself when it is set. |
muted / loop / controls | boolean | false / false / true | Passed to the native <video>. Ignored for source="youtube". |
showOverlay | boolean | true | The title treatment in direct mode: at the foot of the frame until the video is first played, then a hover-only bar at the top. source="video" only — an embed keeps its own title chrome. |
enableGlow | boolean | true | Ambient blur gradient behind the card. |
onPlay / onClose | () => void | undefined | Fire when the player opens and when it is dismissed. Not called in direct mode, which never opens or closes. |
className | string | undefined | Classes on the outer wrapper. All other standard div attributes pass through. |
PlyrPlayer
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | "https://cdn.plyr.io/..." | A media file for html5; a watch URL or bare video id for youtube and vimeo. |
provider | "html5" | "youtube" | "vimeo" | "html5" | Which backend Plyr wraps. All three get the same control bar and keyboard shortcuts. |
mediaType | "video" | "audio" | "video" | HTML5 only. audio drops the video frame and sizes the card to the control bar. |
srcType | string | "video/mp4" | MIME type on the HTML5 <source>. |
posterUrl | string | "https://cdn.plyr.io/..." | Poster for the HTML5 video. Embeds supply their own. |
accentColor | string | undefined | Sets Plyr's --plyr-color-main on the wrapper — control bar, progress fill and focus ring. |
controls | string[] | undefined | Plyr control names in bar order. Omit for Plyr's default set. |
aspectRatio | "16/9" | "21/9" | "4/3" | "1/1" | "16/9" | Frame ratio, forwarded to Plyr's own ratio option in w:h form. |
iconUrl | string | undefined | Overrides Plyr's CDN icon sprite. Point it at a self-hosted plyr.svg to avoid a third-party request. |
autoPlay / muted / loop | boolean | false | Passed to Plyr. autoPlay forces muted, since browsers block the alternative. |
title / subtitle | string | "View From A Blue Moon" / … | Laid over the frame: at the foot until the video is first played (clear of the control bar), then a hover-only bar at the top. An audio player has no picture, so its copy sits above the bar in normal flow instead. |
showOverlay | boolean | true | Whether the title/subtitle overlay renders at all. |
onReady | () => void | undefined | Fires once Plyr has initialised and replaced the raw media element. |
JwPlayer
| Prop | Type | Default | Description |
|---|---|---|---|
libraryUrl | string | JW_DEMO_LIBRARY_URL | JW library script. Defaults to JW's public documentation library — replace it with your own https://cdn.jwplayer.com/libraries/<PLAYER_ID>.js before shipping. |
licenseKey | string | undefined | Only for a self-hosted library build. Cloud-hosted libraries embed their own licence. |
playlistUrl | string | "https://cdn.jwplayer.com/v2/media/hWF9vG66" | A JW playlist or media endpoint. Takes precedence over file. |
file | string | undefined | A direct mp4 or HLS manifest. Also the source of the native fallback player. |
posterUrl | string | "https://images.unsplash..." | Passed to JW as image, and used as the fallback player's poster. |
skin | "default" | "midnight" | "neon" | "sunset" | "minimal" | "default" | Pre-mixed JW colour config for the control bar, menus, time slider and tooltips. |
skinConfig | JwSkinConfig | undefined | Raw JW skin object. Wins over skin when both are set. |
skinUrl / skinName | string | undefined | An external skin stylesheet and the class name it defines, for a fully custom look. |
autoStart / muted / repeat / controls | boolean | false / false / false / true | JW playback flags. autoStart forces muted. |
aspectRatio | "16/9" | "21/9" | "4/3" | "1/1" | "16/9" | Frame ratio, forwarded to JW's aspectratio in w:h form. |
playerOptions | Record<string, unknown> | undefined | Merged last into jwplayer().setup(). The escape hatch for any JW option the props do not cover. |
title / subtitle | string | "Enterprise-grade video delivery" / … | Laid over the frame: at the foot until the video is first played (clear of JW's control bar), then a hover-only bar at the top. |
showOverlay | boolean | true | Whether the title/subtitle overlay renders at all. |
onReady | () => void | undefined | Fires on JW's ready event. A failed load or setup falls back to the native player instead. |