@scarlett-player/vue
| Install | |
|---|---|
npm install @scarlett-player/vue |
|
| Latest Version: | 1.25.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 13, 2026 |
| Links: | GitHub · npm |
@scarlett-player/vue
Vue 3 component wrapper for Scarlett Player.
Docs: scarlettplayer.com/documentation · For AI coding agents: llms.txt (index) and llms-full.txt (every guide as one Markdown file)
Installation
npm install @scarlett-player/vue @scarlett-player/core
Requires @scarlett-player/core 1.18.0 or later (peer range ^1.18.0), and
Vue 3.3 or later.
Quick Start
Basic Component Usage
<template>
<div class="video-container">
<ScarlettPlayer
ref="playerRef"
:src="videoUrl"
:autoplay="false"
:plugins="plugins"
@ready="onPlayerReady"
@play="onPlay"
@pause="onPause"
@timeupdate="onTimeUpdate"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ScarlettPlayerComponent as ScarlettPlayer } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { createNativePlugin } from '@scarlett-player/native';
import { uiPlugin } from '@scarlett-player/ui';
const playerRef = ref();
const videoUrl = ref('https://example.com/video.m3u8');
const plugins = [
createHLSPlugin(),
createNativePlugin(),
uiPlugin({
theme: {
accentColor: '#e50914',
},
}),
];
function onPlayerReady(player) {
console.log('Player ready!', player);
}
function onPlay() {
console.log('Playing');
}
function onPause() {
console.log('Paused');
}
function onTimeUpdate({ currentTime }) {
const duration = playerRef.value?.player?.duration ?? 0;
console.log(`Time: ${currentTime}s / ${duration}s`);
}
// Control player programmatically
async function playVideo() {
await playerRef.value?.play();
}
function pauseVideo() {
playerRef.value?.pause();
}
function seekTo(time: number) {
playerRef.value?.seek(time);
}
</script>
<style scoped>
.video-container {
width: 100%;
max-width: 1280px;
aspect-ratio: 16 / 9;
}
</style>
Using the Composable API
For more control and reactive state management:
<template>
<div>
<div ref="containerRef" class="video-container"></div>
<div class="controls">
<button @click="play" :disabled="playing">Play</button>
<button @click="pause" :disabled="!playing">Pause</button>
<button @click="toggleFullscreen">Fullscreen</button>
<div class="time">
{{ formatTime(currentTime) }} / {{ formatTime(duration) }}
</div>
<input
type="range"
:value="currentTime"
:max="duration"
@input="seek($event.target.value)"
/>
<div class="volume">
<button @click="setMuted(!muted)">
{{ muted ? 'Unmute' : 'Mute' }}
</button>
<input
type="range"
:value="volume"
min="0"
max="1"
step="0.1"
@input="setVolume($event.target.value)"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useScarlettPlayer } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { createNativePlugin } from '@scarlett-player/native';
import { uiPlugin } from '@scarlett-player/ui';
const containerRef = ref<HTMLElement | null>(null);
const {
player,
isReady,
playing,
paused,
currentTime,
duration,
volume,
muted,
fullscreen,
play,
pause,
seek,
setVolume,
setMuted,
toggleFullscreen,
} = useScarlettPlayer({
container: containerRef,
src: 'https://example.com/video.m3u8',
plugins: [
createHLSPlugin(),
createNativePlugin(),
uiPlugin(),
],
autoInit: true,
});
function formatTime(seconds: number): string {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
</script>
<style scoped>
.video-container {
width: 100%;
max-width: 1280px;
aspect-ratio: 16 / 9;
background: #000;
}
.controls {
margin-top: 1rem;
display: flex;
gap: 1rem;
align-items: center;
}
</style>
Component Props
| Prop | Type | Default | Description |
|---|---|---|---|
src |
string |
- | Video source URL |
poster |
string |
- | Poster image URL, shown until the first frame. Reactive: changing it calls setPoster(), unsetting it clears the image |
autoplay |
boolean |
false |
Auto-play video on load |
loop |
boolean |
false |
Loop playback |
volume |
number |
1.0 |
Initial volume (0-1) |
muted |
boolean |
false |
Start muted |
logLevel |
'debug' | 'info' | 'warn' | 'error' |
'warn' |
Console log level |
plugins |
Plugin[] |
[] |
Scarlett Player plugins |
options |
PlayerOptions |
{} |
Additional player options, merged over the props above. Construction-time only (see below) |
src, poster, volume, muted and autoplay are watched and applied to the
running player. options is not: it is merged once, when the player is constructed.
There is no way to re-apply it to a live instance, because plugins cannot be
re-registered, so change one of the watched props at runtime, or re-key the
component to rebuild the player with new options.
Component Events
| Event | Payload | Description |
|---|---|---|
ready |
ScarlettPlayer |
Player initialized and ready |
play |
- | Playback started |
pause |
- | Playback paused |
seeking |
{ time: number } |
Seeking to new time |
seeked |
- | Seek completed |
timeupdate |
{ currentTime: number } |
Time updated |
volumechange |
{ volume: number, muted: boolean } |
Volume/mute changed |
ratechange |
{ rate: number } |
Playback rate changed |
ended |
- | Playback ended |
error |
PlayerError (from the player) or Error (init failed) |
Error occurred |
loaded |
{ src: string, type: string } |
Media loaded |
loadedmetadata |
{ duration: number } |
Metadata loaded |
qualitychange |
{ quality: string, auto: boolean } |
Quality level changed |
qualitylevels |
{ levels: Array<{ id: string, label: string }> } |
Quality levels available |
fullscreenchange |
{ fullscreen: boolean } |
Fullscreen state changed |
destroy |
- | Player destroyed |
Component Methods (via ref)
Access player methods via template ref:
<template>
<ScarlettPlayer ref="playerRef" />
</template>
<script setup>
const playerRef = ref();
// The underlying ScarlettPlayer instance, for anything not proxied below
playerRef.value.player;
// Access methods
playerRef.value.play();
playerRef.value.pause();
playerRef.value.seek(30);
playerRef.value.setVolume(0.5);
playerRef.value.setMuted(true);
playerRef.value.setQuality(2);
playerRef.value.requestFullscreen();
</script>
Available Methods
play()- Start playbackpause()- Pause playbackseek(time: number)- Seek to time in secondsload(src: string, options?: LoadOptions)- Load new source.options.autoplayplays (true) or stays paused (false) for this load only; omitted, the player's current autoplay state decides (theautoplayprop sets it). Setting thesrcprop to the same source in the same update does not load it a second timeunload()- Leave the current source and keep the player: core'sunload()(destroys the provider, resets the player to its empty state, emitssource:unloaded). Ready for the nextload(); a no-op before mountingsetVolume(volume: number)- Set volume (0-1)setMuted(muted: boolean)- Set muted statesetPoster(url: string)- Set the poster ('' clears it)setPlaybackRate(rate: number)- Set playback speedgetQualities()- Get available quality levelssetQuality(index: number)- Set quality level (-1 for auto)getCurrentQuality()- Get current quality indexrequestFullscreen()- Enter fullscreenexitFullscreen()- Exit fullscreentoggleFullscreen()- Toggle fullscreenrequestAirPlay()- Show AirPlay pickerrequestChromecast()- Request Chromecast sessionstopCasting()- Stop castingseekToLive()- Seek to live edge (for live streams)getState()- Get current state snapshotgetPlugin(name: string)- Get plugin instanceregisterPlugin(plugin: Plugin)- Register new plugindestroy()- Destroy player instance
Composable API
The useScarlettPlayer composable provides reactive state and methods:
const {
// Instance
player,
isReady,
error,
// Reactive state
playing,
paused,
currentTime,
duration,
volume,
muted,
bufferedAmount,
fullscreen,
live,
progress,
isBuffering,
// Methods
init,
play,
pause,
seek,
load,
unload,
setVolume,
setMuted,
setPoster,
requestFullscreen,
exitFullscreen,
toggleFullscreen,
getQualities,
setQuality,
getCurrentQuality,
} = useScarlettPlayer(options);
Inside a component's setup() the composable mounts the player for you
(unless autoInit: false) and destroys it before unmount. Called anywhere
else (a store, a plain module, a test), there is no component lifecycle to
hook, so it registers none: call init() yourself and destroy the player
through player.value.destroy() when you are done with it.
load(src, options?) forwards its options to core's load(), so
load(src, { autoplay: false }) keeps the next source paused whatever the
autoplay option says, for that load only. It does nothing before init().
import type { LoadOptions } from '@scarlett-player/core';
const next: LoadOptions = { autoplay: false };
await load('https://cdn.example.com/next.m3u8', next);
unload() calls core's unload(): the provider is destroyed (a WHEP session
closes, HLS stops loading) and the player keeps running for the next load().
playing, paused, currentTime, duration, bufferedAmount and error
reset when the player emits source:unloaded. It does nothing before
init().
Global Plugin Installation
You can register the component globally:
import { createApp } from 'vue';
import { ScarlettPlayerPlugin } from '@scarlett-player/vue';
import App from './App.vue';
const app = createApp(App);
app.use(ScarlettPlayerPlugin);
app.mount('#app');
Then use it without importing:
<template>
<ScarlettPlayer :src="videoUrl" :plugins="plugins" />
</template>
TypeScript Support
Full TypeScript support with exported types:
import type {
ScarlettPlayer,
PlayerOptions,
QualityLevel,
ScarlettPlugin,
EventName,
StateStore,
} from '@scarlett-player/vue';
Examples
Custom Controls
<template>
<div class="player-wrapper">
<ScarlettPlayer
ref="playerRef"
:src="videoUrl"
:plugins="plugins"
@timeupdate="onTimeUpdate"
/>
<!-- Custom UI overlay -->
<div class="custom-controls">
<button @click="playPause">
{{ playing ? 'Pause' : 'Play' }}
</button>
<div class="progress-bar" @click="handleProgressClick">
<div class="progress-fill" :style="{ width: progressPercent + '%' }"></div>
</div>
<select @change="handleQualityChange">
<option value="-1">Auto</option>
<option
v-for="(quality, index) in qualities"
:key="index"
:value="index"
>
{{ quality.label }}
</option>
</select>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ScarlettPlayerComponent as ScarlettPlayer } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { uiPlugin } from '@scarlett-player/ui';
const playerRef = ref();
const videoUrl = ref('https://example.com/video.m3u8');
const playing = ref(false);
const currentTime = ref(0);
const duration = ref(0);
const qualities = ref([]);
const plugins = [
createHLSPlugin(),
uiPlugin({ hideDelay: 3000 }),
];
const progressPercent = computed(() => {
if (duration.value === 0) return 0;
return (currentTime.value / duration.value) * 100;
});
function onTimeUpdate({ currentTime: ct }) {
currentTime.value = ct;
duration.value = playerRef.value?.player?.duration ?? 0;
}
function playPause() {
if (playing.value) {
playerRef.value?.pause();
} else {
playerRef.value?.play();
}
playing.value = !playing.value;
}
function handleProgressClick(e: MouseEvent) {
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
const seekTime = percent * duration.value;
playerRef.value?.seek(seekTime);
}
function handleQualityChange(e: Event) {
const index = parseInt((e.target as HTMLSelectElement).value);
playerRef.value?.setQuality(index);
}
function loadQualities() {
qualities.value = playerRef.value?.getQualities() ?? [];
}
</script>
Live Stream with DVR
<template>
<div>
<ScarlettPlayer
ref="playerRef"
:src="liveStreamUrl"
:autoplay="true"
:plugins="plugins"
@loadedmetadata="onMetadataLoaded"
/>
<button v-if="isLive" @click="goToLive">
Go to Live
</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ScarlettPlayerComponent as ScarlettPlayer } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { uiPlugin } from '@scarlett-player/ui';
const playerRef = ref();
const liveStreamUrl = ref('https://example.com/live.m3u8');
const isLive = ref(false);
const plugins = [
createHLSPlugin({ lowLatencyMode: true }),
uiPlugin(),
];
function onMetadataLoaded() {
// The payload is { duration }; the live flag lives on the player instance.
isLive.value = playerRef.value?.player?.live ?? false;
}
function goToLive() {
playerRef.value?.seekToLive();
}
</script>
Browser Support
- Chrome 80+
- Firefox 78+
- Safari 14+
- Edge 80+
License
MIT
Credits
Built with Scarlett Player - Modular. Extensible. Yours.
Related Packages
Version History
| Version | Released | License |
|---|---|---|
| 1.25.0 | MIT | |
| 1.24.0 | MIT | |
| 1.23.0 | MIT | |
| 1.22.0 | MIT | |
| 1.21.0 | MIT | |
| 1.20.0 | MIT | |
| 1.19.3 | MIT | |
| 1.19.2 | MIT | |
| 1.19.1 | MIT | |
| 1.19.0 | MIT | |
| 1.18.0 | MIT | |
| 1.17.0 | MIT | |
| 1.16.3 | MIT | |
| 1.16.2 | MIT | |
| 1.16.1 | MIT | |
| 1.16.0 | MIT | |
| 1.15.4 | MIT | |
| 1.15.3 | MIT | |
| 1.15.2 | MIT | |
| 1.15.1 | MIT | |
| 1.15.0 | MIT | |
| 1.14.0 | MIT | |
| 1.13.0 | MIT | |
| 1.12.0 | MIT | |
| 1.11.0 | MIT | |
| 1.10.0 | MIT | |
| 1.9.0 | MIT | |
| 1.8.1 | MIT | |
| 1.8.0 | MIT | |
| 1.7.0 | MIT |
Showing the latest 30 of 50. See every release on npm