Every video source, one React component
Local files, HLS, YouTube, Vimeo and Bilibili behind a single API — with captions, chapters, playlists and analytics callbacks already in the box. 14.0 kB gzipped, and nothing else in your lockfile.
npm i vplayer-reactcontrolsVariant="minimal" — One row, scrubber between elapsed and remaining time
No More Barrier to Cross
Local MP4 playlist with chapters, captions, ref API, and all callbacks
Ref API Controls
Feature Toggles
Track
1 / 2
Chapter
—
Volume
100%
Buffered
Milestones
Callback Log
Interact with the player to see callback events here
Brand Accent Color
14.0kB
minified + gzipped
0
runtime dependencies
5
source types, one component
18+
React, as a peer dependency
Everything a real player needs
Not a <video> tag with a skin on it — the parts you would otherwise build yourself, in the same component.
Sources
- One API, five source types
- Local and remote media files, HLS manifests, YouTube, Vimeo and Bilibili — the component works out which it is and renders the right thing.
- Lazy embeds
- Platform iframes stay unmounted behind the poster until someone actually presses play, so a page full of players costs one image each.
- Playlists
- Pass an array for prev/next and auto-advance, or drive the index yourself with activeIndex and onIndexChange.
- Native HLS
- Plays where the browser supports it, and surfaces a clear error overlay where it doesn’t — instead of failing silently.
The viewing experience
- Three control skins
- Classic, minimal and floating carry identical controls — the difference is layout and surface. Each adapts down to a 300px player without a control ever losing its hit area.
- Captions that clear the controls
- Cues are drawn inside the picture and lift above the bar while it’s on screen, so they stay readable — even for a wide clip fullscreened on a portrait phone.
- Chapters and scrub previews
- Tick marks with hover labels, Shift+Arrow chapter jumps, and sprite-sheet frame previews while scrubbing.
- Keyboard and touch, properly
- Scoped shortcuts that never hijack the page, 44px hit areas, touch scrubbing, PiP, and a reduced-motion path for every transition.
- Errors that recover
- A failed load names what the viewer can check — missing file, blocked by the server, unplayable codec — and offers a retry instead of a dead end.
The developer experience
- Zero runtime dependencies
- Pure React and inline styles. No CSS file to import, nothing to configure, nothing else pulled into your lockfile.
- Analytics callbacks
- onPlay, onSeek, onBuffer, onMilestone, onChapterChange, onVolumeChange, onError and a dozen more — enough to instrument playback without a wrapper.
- Ref API
- play, pause, seek, setVolume, toggleMute, toggleFullscreen, and an escape hatch to the underlying media element.
- Controlled or uncontrolled
- playing, volume, muted, playbackRate and the playlist index all accept controlled props — or leave them off and the player keeps its own state.
- Clip mode and resume
- initialTime resumes where a viewer left off, endTime plays an excerpt out of a longer file, and persistVolume remembers the level across reloads.
- Typed and server-safe
- Full TypeScript types, matching ESM and CJS declarations, and a “use client” directive so importing from a Server Component just works.
Working in a minute
Install, import, pass a URL. Everything below that is optional.
npm i vplayer-react- React 18 or newer, as a peer dependency
- No stylesheet to import and nothing to configure
- Ships ESM and CJS with matching type declarations
- Carries “use client”, so a Server Component can import it
import { VPlayer } from 'vplayer-react';
export default function Page() {
return (
<VPlayer
src="https://example.com/video.mp4"
poster="/poster.jpg"
title="My Video"
accentColor="#ff5705"
controlsVariant="minimal"
/>
);
}One component, five kinds of URL. Platform links become lazy embeds behind a poster; media files and HLS manifests get the full custom control bar.
// Local or remote media file
<VPlayer src="/clip.mp4" />
// HLS — plays natively where the browser supports it,
// with a clear error overlay where it doesn’t
<VPlayer src="https://cdn.example.com/master.m3u8" />
// Platform links — lazy-loaded iframes behind the poster
<VPlayer src="https://youtu.be/OZjOHE_Y8vY" />
<VPlayer src="https://vimeo.com/347119375" />
<VPlayer src="https://www.bilibili.com/video/BV1Tm421n7WT/" />
// Check before you render
VPlayer.canPlay("https://youtu.be/OZjOHE_Y8vY"); // trueThe full prop reference — every callback, every type — lives in the README.
Keyboard, scoped
Shortcuts fire only while the player has focus, so a page with three players — or a search box — never loses a keystroke to the video. Every binding is remappable through the keymap prop, and any of them can be switched off.
- Space / K
- Play or pause
- F
- Toggle fullscreen
- M
- Toggle mute
- ← / →
- Rewind or forward 5s
- Shift + ← / →
- Previous or next chapter
- ↑ / ↓
- Volume up or down
- 0–9
- Seek to 0%–90%
- < / >
- Decrease or increase speed
- ?
- Toggle the shortcuts overlay
- Esc
- Close menus and overlays
Questions
The ones worth answering before you install anything.
Reading this with an agent? There is a markdown mirror of the whole page at /llms-full.txt, and a short index at /llms.txt.
Which React video player handles MP4 and YouTube in the same component?
vplayer-react does. Pass any URL to src and the component works out what it is: media files and HLS manifests render with the full custom control bar, while YouTube, Vimeo and Bilibili links become lazy-loaded embeds behind the poster. VPlayer.canPlay(url) tells you in advance whether a URL is recognised.
How large is vplayer-react?
14.0 kB minified and gzipped, with zero runtime dependencies — nothing but React itself enters your lockfile, and there is no stylesheet to import. The component is styled with inline styles plus one small injected stylesheet for hover, focus and reduced-motion rules.
Does it work with Next.js and React Server Components?
Yes. The published bundles carry the "use client" directive, so importing vplayer-react directly inside a Server Component works without a wrapper. It ships both ESM and CommonJS builds with matching TypeScript declarations, and requires React 18 or newer as a peer dependency.
Does it support HLS streams?
Yes, where the browser plays HLS natively — Safari, iOS and some Chromium builds. Pass an .m3u8 URL like any other source. In browsers without native support the player surfaces a clear error overlay rather than failing silently, so you can decide whether to ship an HLS polyfill alongside it.
How do I add subtitles or closed captions?
Pass a tracks array of WebVTT files and a CC picker appears in the control bar. The player renders the cues itself rather than leaving them to the browser, so they sit inside the picture, rise to clear the control bar while it is on screen, and stay put when a widescreen clip is fullscreened on a portrait phone. captionStyle restyles them without any CSS file.
Is it accessible?
Keyboard shortcuts are scoped to the player, so a page with three players — or a search box — never loses a keystroke to the video. Every control is a real button with an accessible name and a 40×44px hit area, the seek and volume sliders implement the ARIA slider pattern, hidden controls leave the tab order, and every transition has a prefers-reduced-motion path.
Can I drive playback from my own state?
Yes. playing, volume, muted, playbackRate and the playlist index all accept controlled props, and there is an imperative handle with play(), pause(), seek(), setVolume(), toggleMute(), toggleFullscreen() and an escape hatch to the underlying media element. Leave the props off and the player keeps its own state.
Can I change how the controls look?
controlsVariant picks between three skins carrying identical controls: classic (full-width scrim with the scrubber on its own row), minimal (one row with the scrubber inline between elapsed and remaining time) and floating (that row inside a detached, blurred pill). accentColor and iconColor handle brand colour, and each skin adapts down to a 300px-wide player.