npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@munsonlabs/video-player

v0.0.5

Published

A Vue 3 video player supporting YouTube, Vimeo, Dailymotion, Brightcove, JW Player, and plain HTML5. Includes a sticky stage player, lazy loading, custom actions, IMA ad support, and web component exports.

Downloads

515

Readme

@munsonlabs/video-player

A Vue 3 video player supporting YouTube, Vimeo, Dailymotion, Brightcove, JW Player, and plain HTML5. Includes a sticky stage player, lazy loading, custom actions, IMA ad support, and web component exports.

Installation

npm install @munsonlabs/video-player video.js

Vue Usage

Basic player

<script setup>
import { VideoItem } from '@munsonlabs/video-player'
import '@munsonlabs/video-player/style'
</script>

<template>
  <VideoItem
    title="Big Buck Bunny"
    video-url="https://cdn.jwplayer.com/videos/O5chtspP-4VHSaSK0.mp4"
    platform="html5"
    poster="https://storage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg"
  />
</template>

Stage player

VideoStage is a sticky full-width player that receives videos from VideoItem components anywhere on the page via window events. When the stage scrolls out of view it minifies to a pip in the bottom-right corner.

<script setup>
import { VideoStage, VideoItem } from '@munsonlabs/video-player'
import '@munsonlabs/video-player/style'
</script>

<template>
  <VideoStage @state-change="onStateChange" />

  <VideoItem v-for="video in videos" :key="video.videoUrl" v-bind="video" />
</template>

VideoItem / VideoPlayer Props

| Prop | Type | Default | Description | | -------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | videoUrl | string | — | Required. The video URL or platform-specific URI | | platform | string | — | youtube · vimeo · dailymotion · brightcove · jwplayer · html5 | | title | string | '' | Video title | | poster | string | '' | Poster image URL | | aspectRatio | string | '16:9' | e.g. '16:9', '9:16', '4:3' | | autoplay | boolean | false | Autoplay on mount (mutes automatically) | | muted | boolean | false | Start muted | | lazy | boolean | true* | Show placeholder until clicked | | nativeUi | boolean | false | Use the platform's native controls (YouTube, Vimeo, Dailymotion only) | | autoStage | boolean | false | Immediately send this video to the stage on mount | | playbackRate | number | 1 | Initial playback rate | | adTagUrl | string | '' | VAST or VMAP ad tag URL | | imaOptions | object | {} | Extra options passed to the IMA plugin (e.g. { customParameters: { section: 'news' } }) | | techOptions | object | {} | Extra options passed directly to Video.js | | payload | object | {} | Arbitrary data attached to every state-change event | | action | PlayerAction | null | Button shown in the player HUD — see Actions | | focusMode | boolean | false | The "show controls" button centers the video and renders controls below it — see Focus mode |

* lazy only has an effect on VideoItem (default true), which is what actually implements the placeholder-until-clicked behavior. VideoPlayer accepts the prop for type compatibility but defaults to false and never reads it — using <VideoPlayer> directly always mounts the real player immediately, regardless of lazy.

Platform URLs

| Platform | URL format | | ------------- | -------------------------------------------------------------------------------------------------------- | | youtube | https://www.youtube.com/watch?v=VIDEO_ID | | vimeo | https://vimeo.com/VIDEO_ID | | dailymotion | https://www.dailymotion.com/video/VIDEO_ID | | brightcove | brightcove://ACCOUNT_ID/PLAYER_ID/embed/VIDEO_ID | | jwplayer | https://cdn.jwplayer.com/videos/... · https://cdn.jwplayer.com/manifests/... · jwplayer://MEDIA_ID | | html5 | Any direct MP4 / HLS URL |


Events

state-change

Emitted by VideoItem, VideoPlayer, and VideoStage.

interface StateChangeEvent {
  type:
    | 'play'
    | 'pause'
    | 'ended'
    | 'seeked'
    | 'error'
    | 'adstart'
    | 'adend'
    | 'volumechange'
    | 'ratechange'
    | 'firstQuartile'
    | 'midpoint'
    | 'thirdQuartile'
    | 'focusstart'
    | 'focusend'
  currentTime: number
  duration: number
  src: string
  error?: { code: number; message: string } | null
  isMuted?: boolean // volumechange only
  playbackRate?: number // ratechange only
  payload?: Record<string, unknown>
}

firstQuartile / midpoint / thirdQuartile fire once each as playback crosses 25%, 50%, and 75% of the video's duration — useful for analytics integrations that expect IAB-style progress milestones. They reset and can fire again on loop restart.

focusstart / focusend fire when focus mode is entered/exited (only relevant if focusMode is set).

On error, the player shows a "Retry" button that re-attempts loading the current source. You can also trigger this yourself via a template ref on VideoPlayer, which exposes a retry() method along with togglePlay(), seek(), toggleMute(), setVolume(), toggleFullscreen(), toggleLoop(), and setPlaybackRate(). Note this is on VideoPlayer specifically — VideoItem doesn't forward a ref to it, since it may be showing a lazy placeholder instead:

<script setup>
import { ref } from 'vue'
const player = ref(null)
</script>

<template>
  <VideoPlayer ref="player" video-url="..." platform="html5" />
  <button @click="player.retry()">Retry</button>
</template>
<VideoItem video-url="..." platform="html5" @state-change="onStateChange" />

Focus mode

Set focusMode to change what the "show controls" button (in the HUD row) does: instead of overlaying the expanded controls on top of the video, the video centers itself on the page and the controls bar renders below it, dimming the rest of the page behind a backdrop.

<VideoItem video-url="..." platform="youtube" focus-mode />
  • The video's original spot in the page keeps its size (an empty placeholder box) so surrounding layout doesn't jump while it's centered.
  • Clicking the backdrop, pressing Escape, or the close button in the controls bar all return it to its original position.
  • The move is animated via the View Transitions API where supported, falling back to an instant toggle otherwise.
  • The video element itself is never moved in the DOM (no Teleport) — it's purely a CSS position: fixed toggle — so YouTube/Vimeo/Dailymotion iframes are never reloaded by entering or exiting focus mode.

Actions

The action prop adds a button to the player HUD. Pass a built-in string or a custom action object.

Built-in actions

<VideoItem action="mute" ... />
<VideoItem action="loop" ... />

Custom action

interface CustomAction {
  icon: string // SVG string
  label: string
  onClick: () => void
}
<script setup>
const saveAction = {
  icon: `<svg .../>`,
  label: 'Save',
  onClick: () => console.log('saved'),
}
</script>

<template>
  <VideoItem :action="saveAction" ... />
</template>

Ads (IMA / VAST / VMAP)

Pass an adTagUrl to enable pre-roll, mid-roll, or VMAP ad schedules via the Google IMA SDK. The SDK is loaded automatically on demand.

<VideoItem video-url="https://..." platform="html5" ad-tag-url="https://pubads.g.doubleclick.net/..." />

Custom IMA parameters

Use imaOptions to pass any option supported by videojs-ima, including customParameters for cust_params:

<VideoItem
  video-url="https://..."
  ad-tag-url="https://pubads.g.doubleclick.net/..."
  :ima-options="{ customParameters: { section: 'news', topic: 'sport' } }"
/>

Ads are not supported on YouTube, Vimeo, or Dailymotion.


Theming the HUD buttons

The three buttons in the HUD row — the action button (mute/loop/custom/save), the central play/pause button, and the "show controls" button — read their background and icon color from two CSS custom properties, falling back to the default frosted-glass look when unset. The expanded controls popup (opened via the "show controls" button) is unaffected and keeps its own fixed styling.

Set the variables on any ancestor of the player — e.g. a wrapper <div> or :root — to restyle all three buttons at once:

.my-video-wrapper {
  --mvp-btn-bg: #e11d48;
  --mvp-btn-color: #fff;
}

Icons use fill="currentColor", so --mvp-btn-color recolors the icon along with --mvp-btn-bg for the background. Hover state is a filter: brightness() lift on top of --mvp-btn-bg, so it works for both the default translucent look and a solid theme color.


Window Events

VideoItem and VideoStage communicate via window custom events. You can dispatch these yourself to control the stage from outside Vue.

| Event | Detail | Description | | -------------- | --------------------------------------------------------- | ---------------------------------------------- | | video-select | VideoSelectDetail | Load a video into the stage | | video-toggle | { videoUrl: string } | Play/pause a specific video in the stage | | video-state | { currentVideoUrl: string \| null, isPlaying: boolean } | Dispatched by the stage when its state changes |

// Load a video into the stage
window.dispatchEvent(
  new CustomEvent('video-select', {
    detail: {
      videoUrl: 'https://...',
      platform: 'html5',
      title: 'My Video',
      poster: 'https://...',
      autoplay: true,
    },
  }),
)

// Listen for stage state changes
window.addEventListener('video-state', (e) => {
  console.log(e.detail.currentVideoUrl, e.detail.isPlaying)
})

Web Component Usage

Import the element bundle to register all components as native custom elements. Vue is an external dependency — use an import map to provide it:

<script type="importmap">
  {
    "imports": {
      "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
    }
  }
</script>
<script type="module" src="@munsonlabs/video-player/element"></script>

<video-item
  video-url="https://cdn.jwplayer.com/videos/O5chtspP-4VHSaSK0.mp4"
  platform="html5"
  title="Big Buck Bunny"
  poster="https://storage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg"
></video-item>

Available elements

| Element | Vue equivalent | | --------------------- | -------------------- | | <video-item> | <VideoItem> | | <video-stage> | <VideoStage> | | <video-player> | <VideoPlayer> | | <video-placeholder> | <VideoPlaceholder> |

Passing objects as web component attributes

Boolean and string props map directly to HTML attributes. Object props (imaOptions, techOptions, payload) can be passed as inline JSON strings — Vue's custom element runtime parses them automatically:

<video-item
  video-url="https://..."
  platform="html5"
  ad-tag-url="https://..."
  ima-options='{"customParameters":{"section":"news","topic":"sport"}}'
  payload='{"articleId":"123"}'
></video-item>

For action (which contains a function), set it via JavaScript:

const player = document.querySelector('video-item')
player.action = { icon: '<svg.../>', label: 'Save', onClick: () => {} }

Listening to events

const player = document.querySelector('video-item')
player.addEventListener('state-change', (e) => {
  console.log(e.detail)
})

Peer Dependencies

| Package | Version | | ---------- | ---------------------- | | video.js | >=8 | | vue | >=3 (Vue usage only) |