A fully customizable and responsive Web Component that supports:
- YouTube videos (including Shorts)
- Vimeo videos
- Self-hosted videos (MP4, WebM, etc.)
- Poster images in AVIF, WebP, JPG, PNG
- Optional fullscreen, custom play button, and auto-sizing
The home page for this project is at https://github.com/johnfmorton/video-player where you will find the source code.
You can view a working demo of this component at https://johnfmorton.github.io/video-player/.
This web-component was built because I needed a video player that could handle YouTube, Vimeo, and self-hosted videos. The project required being able to have custom poster frames which would load the video player upon click.
I also wanted a self-contained component with a simple API that would allow me to easily add videos to my projects without having to worry about the underlying implementation details.
This is the result.
The video-player
component is available via npm at https://www.npmjs.com/package/@morton-studio/video-player.
Install it with npm:
npm install @morton-studio/video-player
Next, import and register the component in your JavaScript file. Note that you can register the component with a custom tag name when using npm.
import { registerVideoPlayer } from '@morton-studio/video-player'
registerVideoPlayer()
// If you want to customize the tag name, pass in a string:
// registerVideoPlayer('my-video-player')
If you prefer not to use npm, you can include the component directly in your HTML file. This will load the latest version from a CDN and register the component automatically.
<script type="module" src="https://cdn.jsdelivr.net/npm/@morton-studio/video-player@latest/dist/video-player.es.js"></script>
<script type="module" src="https://unpkg.com/@morton-studio/video-player@latest/dist/video-player.es.js"></script>
If you are using the CDN version, you don't need to do anything else. The component will be registered automatically using the default tag name <video-player>
in your HTML.
Then use it in your HTML:
<video-player src="video.mp4"></video-player>
You may include the YouTube and Vimeo APIs in your HTML file if you want to use their features directly in other parts of your application.
This is optional, as the component will automatically load them if needed by looking on the Window object for YT
or Vimeo
. If either API is not required, the component will not load them. For example, if you have no Vimeo URLs in video-player
components on your page, the Vimeo API will not be loaded.
<!-- YouTube iFrame API, if needed -->
<script src="https://www.youtube.com/iframe_api"></script>
<!-- Vimeo API, if needed -->
<script src="https://player.vimeo.com/api/player.js"></script>
Automatically determines video type based on the src
URL:
- YouTube URLs (incl. Shorts):
https://www.youtube.com/watch?v=...
,https://youtu.be/...
,https://youtube.com/shorts/...
- Vimeo:
https://vimeo.com/...
- Self-hosted: anything else (assumes direct video file)
Supports predefined aspect ratios:
-
16x9
(default) 4x3
1x1
9x16
Use the aspect-ratio
attribute:
<video-player aspect-ratio="4x3"></video-player>
Use a single image:
<video-player poster="/images/poster.jpg"></video-player>
Or use multiple formats:
<video-player posters='{
"avif": "/images/poster.avif",
"webp": "/images/poster.webp",
"jpg": "/images/poster.jpg",
"png": "/images/poster.png"
}'></video-player>
Use the posteralt
attribute to improve accessibility:
<video-player posteralt="Preview image for demo clip"></video-player>
Adds a centered play icon on the poster frame:
<video-player playbutton></video-player>
Use the allowfullscreen
attribute:
<video-player allowfullscreen></video-player>
The component normalizes events across different video player implementations, ensuring a unified experience. Supported events include:
video-play
video-pause
video-ended
Each event is dispatched with a detail
object containing the following properties:
-
type
: The type of video service (e.g.,youtube
,vimeo
,self-hosted
). -
src
: The source URL of the video. -
currentTime
: The current playback time in seconds, if available. -
duration
: The total duration of the video in seconds, if available.
This allows developers to handle events consistently, regardless of the underlying video player technology.
Make the component stretch to fill its container:
<video-player autosize></video-player>
This is useful inside flex
or grid
layouts.
You can customize the appearance of the play button using CSS variables:
video-player {
--play-button-bg: rgba(0, 0, 0, 0.7); /* Button background */
--play-button-arrow: white; /* Triangle color */
--play-button-bg-hover: rgba(0, 0, 0, 0.9); /* Background on hover */
}
These variables let you customize the style without modifying the component directly.
This component has built-in accessibility features:
- Poster images have customizable
alt
text. - All clickable elements (poster, play button) are keyboard accessible via
Enter
andSpace
keys. - The poster is focusable and labeled with
role="button"
andaria-label
. - Uses semantic
<button>
for the play overlay. -
iframe
embeds include atitle
attribute for screen reader clarity.
Use the sources
attribute (as a JSON string):
<video-player sources='[
{ "src": "/video.mp4", "type": "video/mp4" },
{ "src": "/video.webm", "type": "video/webm" }
]'></video-player>
<video-player
src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
posters='{
"webp": "/images/poster.webp",
"png": "/images/poster.png"
}'
posteralt="Preview frame for YouTube video"
playbutton
allowfullscreen
aspect-ratio="16x9">
</video-player>
<video-player
sources='[
{ "src": "/video.mp4", "type": "video/mp4" },
{ "src": "/video.webm", "type": "video/webm" }
]'
posters='{
"avif": "/images/poster.avif",
"jpg": "/images/poster.jpg"
}'
posteralt="Preview frame for self-hosted video"
playbutton
allowfullscreen
autosize
aspect-ratio="9x16">
</video-player>
Include the JS file via <script type="module">
or package it into your build system.
<script type="module" src="/path/to/video-player.js"></script>
Or import and register manually:
import VideoPlayer, { registerVideoPlayer } from './video-player.js';
registerVideoPlayer();
You can also customize the tag name:
registerVideoPlayer('my-video-player');
- Written in plain TypeScript but compiles to ES6 for compatibility.
- Uses Shadow DOM
- No external dependencies
- Easily extensible for subtitles, controls, etc.
All notable changes to this project will be documented in the CHANGELOG.md file.
MIT
Created by John F Morton. Contributions welcome!