Responsive Video Poster

Custom video placeholder with lazy load for better performance

Responsive Video Poster logo


  • Full styling control of video placeholders
  • An image/picture is used for the poster which allows the use of src-set
  • Videos and embeds are lazy loaded to reduce page load
  • Embeds are prefetched on user interaction to improve performance
  • HTML Web Component version



The default setup for the plugin with an image and a video.
The poster is the first frame of the video.


Embedded video from a third-party site e.g. Youtube, Vimeo, etc.
The poster is the middle frame of the video.

No image

The placeholder is styled instead of using an image.

Web component


$ npm install responsive-video-poster --save-dev


Import or add the JS into your project, add the elements to your HTML and initialize the plugin if needed.


import { ResponsiveVideoPoster, ResponsiveVideoPosterAuto } from 'responsive-video-poster.js';


<script src="responsive-video-poster.js" type="module"></script>

Web Component

import { ResponsiveVideoPoster } from 'responsive-video-poster-wc.js';


<script src="responsive-video-poster-wc.js" type="module"></script>

Initialize JS

// Initialize a single element
const responsiveVideoPosterDefault = ResponsiveVideoPoster({ 
  selector: '#responsive-video-poster--default'

// Initialize all elements with default options, these can be overridden by reinitializing or by data attributes on the element.



<div class="responsive-video-poster">
    <button class="rvp-placeholder" aria-label="Play video">
      <span class="rvp-button" aria-hidden="true"><svg class="rvp-button-icon"> ... </svg></span>
      <img srcset="images/720.jpg 720w, images/1080.jpg 1080w" src="images/1080.jpg" class="rvp-poster" width="1080" height="608" loading="lazy">
    <video src="videos/video.mp4" class="rvp-video" preload="none" width="1080" height="608"></video>

Web Component



The options can be set via initialization in the JS or by attributes on the element, which will be given the highest priority. The property is changed from camel case to dash case with with the prefix 'rvp' added for the JS version e.g. "activeClass" to "rvp-active-class".

<div class="responsive-video-poster" data-rvp-active-class="is-open">
<responsive-video-poster active-class="is-open">
Property Default Type Description
selector '.responsive-video-poster' String || Element Container element selector.
placeholderSelector '.rvp-placeholder' String Placeholder element selector.
posterSelector '.rvp-poster' String Poster element selector.
videoSelector '.rvp-video' String Video element selector.
animClass 'is-anim' String CSS class to transition the video placeholder between states.
activeClass 'is-active' String CSS class when placeholder is transitioned and the video is playing.
playDelay 'transition' Integer(ms) || 'transition' Delay playing the video by set time or wait for the placeholder transition to finish.
playDelayOffset 0 Integer(ms) Reduces playDelay to start loading the video before the placeholder has finished transitioning.
hideControlsOnLoad true Boolean Hide video controls while transitioning placeholder.
hideControlsOnFirstPlay false Boolean Hide video controls on first video play.
preConnections [] Array || Comma seperated list Domains to preconnect to for loading an embed. Youtube and Vimeo are automatically preconnected.


Property Type Description
instance.playVideo() Method Plays the video.
instance.getInfo() Object Returns the element and its properties.

Performance optimizations

Responsive image techniques(srcset, sizes, source, etc) are used to load the most appropriate image. Native lazy loading is used on the responsive poster image to reduce initial page load. The plugin should also work normally with lazy load plugins.

The video element includes preload="metadata" or preload="none" so the video is not loaded until the user interacts with it. This will reduce the initial page load but may create lag in playback for some users. This can be removed if not needed.

The embedded video includes srcdoc="" so the video and third-party scripts are not loaded until the user interacts with it. The 'preConnections' option can be used to pass domains to start pre-connect connections for loading an embedded video. this happens when the user hovers/taps on the video using the pointerover event. For example Youtube could use ['', ''].


A 'playVideo' event is started once a user clicks the video placeholder. The event is ended once the placeholder has become inactive and the video starts playing.

document.querySelector('#responsive-video-poster--default').addEventListener('playVideo', (event) => { 
  console.log(`Action: ${event.detail.action}`);

You can also attach this to the document.

document.addEventListener('playVideo', (event) => { 
  console.log(`Target: ${'#responsive-video-poster--default')}`, `Action: ${event.detail.action}`);


Supports all modern browsers at the time of release.

Demo site

Clone the repo from Github and run the commands below.

$ npm install
$ npm run dev


Lazy loading of an embedded video by Arthur Corenzan. Lazy load third-party resources with facades. HTML Web Components by Jim Nielsen
Covverr videos of Lago di braies and Lofoten rocks. Youtube video of Tustan Karpaty mountains.