Embedding videos with standard <iframe> tags can dramatically slow down your site and consume large amounts of data. Each iframe loads the full video player and related resources immediately-even if the user never interacts with it. On pages with several videos, this can add hundreds of megabytes to the initial page load, resulting in a sluggish and costly experience, especially for users on mobile devices or limited networks.
The <lazy-video> component solves this by loading only a lightweight thumbnail and play button at first. The actual video player is loaded only when the user clicks play (or when the video scrolls into view if autoload is enabled). This keeps your pages fast, responsive, and bandwidth-friendly.
To get started, include the script on your page and use the custom element as shown below:
<lazy-video
src="https://www.youtube.com/embed/wPr3kws2prM"
title="Till We Have Faces by Silent Planet">
</lazy-video>
Always add a title for accessibility and better alt text on thumbnails.
| Platform | URL Pattern | Notes |
|---|---|---|
| YouTube |
|
Full support for thumbnails and parameters. |
| Bitchute |
|
Custom thumbnails are only needed if autoload is disabled. |
| Attribute | Description | Default |
|---|---|---|
| src | Video embed URL (required) | N/A |
| title | Video title | "Video" |
| width | Width in pixels or percent | 100% (responsive) |
| height | Height in pixels | 16:9 ratio |
| thumbnail | Custom thumbnail URL | Auto-detected per platform |
| thumbnail-quality | YouTube thumbnail quality (default, hq, mq, sd, maxres) | Auto (maxres on desktop, hq on mobile) |
| service | Force a specific service (youtube, bitchute) | Auto-detected |
| sandbox | Extra security for the iframe. Restricts what the embedded player can do. See MDN for details. | allow-scripts allow-same-origin allow-popups allow-forms allow-presentation |
| no-cookie | Use youtube-nocookie.com for YouTube (privacy-friendly) | true |
| autoload | Load video when scrolled into view | false (YouTube), true (Bitchute) |
| hide-title | Hide the video title bar | false |
| align | Set alignment (left, right, center) | center |
| container-fit | Make video fill the container (FitVids style) | false |
Warning: Using autoload with many videos on one page can impact performance as users scroll. Use with care!
Note: With container-fit, the component overrides max-width to 100% and sets max-height to auto, making it fill its container while keeping the aspect ratio.
You can customize the look of <lazy-video> using CSS variables:
lazy-video {
--lv-max-width: 600px;
--lv-border-radius: 8px;
--lv-play-button-color: #f00;
--lv-play-button-bg: rgba(0, 0, 0, 0.7);
--lv-show-title: none;
}
| CSS Variable | Description | Default |
|---|---|---|
| --lv-max-width | Maximum width of the video | 560px |
| --lv-aspect-ratio | Aspect ratio | 16 / 9 |
| --lv-display | Display type | block |
| --lv-position | CSS position | relative |
| --lv-border-radius | Border radius for the container | 0 |
| --lv-margin | Container margin | 0 auto |
| --lv-margin-left | Margin for left alignment | 0 |
| --lv-margin-right | Margin for right alignment | 0 0 0 auto |
| --lv-margin-center | Margin for center alignment | 0 auto |
| --lv-align | Set alignment (left, right, center) | center |
| --lv-background | Background color | #000 |
| --lv-thumbnail-opacity | Thumbnail opacity | 0.85 |
| --lv-thumbnail-hover-opacity | Opacity on hover | 1 |
| --lv-thumbnail-object-fit | Object-fit for thumbnail | cover |
| --lv-play-button-width | Play button width | 68px |
| --lv-play-button-height | Play button height | 48px |
| --lv-play-button-bg | Play button background | rgba(33, 33, 33, 0.8) |
| --lv-play-button-bg-hover | Play button hover background | rgba(230, 33, 23, 1) |
| --lv-play-button-color | Play button arrow color | rgba(255, 255, 255, 0.9) |
| --lv-play-button-radius | Play button border radius | 8px |
| --lv-play-button-arrow-size | Play button arrow size | 12px 0 12px 20px |
| --lv-title-padding | Title bar padding | 10px 12px |
| --lv-title-bg | Title background | rgba(0, 0, 0, 0.75) |
| --lv-title-color | Title text color | white |
| --lv-title-font-family | Title font family | Roboto, Arial, sans-serif |
| --lv-title-font-size | Title font size | 18px |
| --lv-title-font-weight | Title font weight | 500 |
| --lv-title-line-height | Title line height | 1.2 |
| --lv-focus-outline | Focus outline | 2px solid #4285F4 |
| --lv-focus-outline-offset | Focus outline offset | 2px |
| --lv-show-title | Show/hide title bar (use 'none' to hide) | block |
| --lv-timestamp-right | Timestamp right position | 10px |
| --lv-timestamp-bottom | Timestamp bottom position | 10px |
| --lv-timestamp-bg | Timestamp background | rgba(0, 0, 0, 0.7) |
| --lv-timestamp-color | Timestamp text color | white |
| --lv-timestamp-padding | Timestamp padding | 2px 6px |
| --lv-timestamp-radius | Timestamp border radius | 3px |
| --lv-timestamp-font-size | Timestamp font size | 12px |
| --lv-timestamp-font-family | Timestamp font family | system-ui, sans-serif |
| --lv-loading-bg | Loading background | rgba(0,0,0,0.7) |
| --lv-loading-color | Loading text color | white |
| --lv-loading-font-family | Loading font family | system-ui, sans-serif |
| --lv-fallback-bg | Fallback background | #1a1a1a |
| --lv-fallback-color | Fallback text color | white |
| --lv-fallback-font-family | Fallback font family | system-ui, sans-serif |
| --lv-fallback-font-size | Fallback font size | 14px |
<lazy-video
src="https://www.youtube.com/embed/wPr3kws2prM"
title="Till We Have Faces by Silent Planet"
width="50%"
height="260px"
thumbnail-quality="maxres">
</lazy-video>
<lazy-video
src="https://www.bitchute.com/video/zSfeNPF-OpY"
title="Trump Assassination Attempt Documents LOCKED Away. What are they Hiding?"
autoload="false"
thumbnail="https://static-3.bitchute.com/live/cover_images/nDPZqzyLkFKW/zSfeNPF-OpY_640x360.jpg">
</lazy-video>
With autoload="false" on Bitchute, users need to click twice: once to load the player, and again to play. This saves bandwidth but may be less convenient.
<lazy-video
src="https://www.bitchute.com/video/zSfeNPF-OpY"
title="Trump Assassination Attempt Documents LOCKED Away. What are they Hiding?">
</lazy-video>
<div style="max-width: 100%; width: 100%;">
<lazy-video
src="https://www.youtube.com/embed/wPr3kws2prM"
title="Responsive container example"
container-fit="true">
</lazy-video>
</div>
container-fit="true" makes the video fill its parent container while keeping the aspect ratio. Great for fluid layouts.
<lazy-video
src="https://www.youtube.com/embed/wPr3kws2prM"
title="Hidden title example"
hide-title>
</lazy-video>
/* Hide titles for all videos */
lazy-video {
--lv-show-title: none;
}
/* Hide titles for a group */
.article-videos lazy-video {
--lv-show-title: none;
}
/* Set alignment for all videos */
lazy-video {
--lv-align: left;
}
/* Responsive alignment */
@media (max-width: 768px) {
lazy-video {
--lv-align: center;
}
}
/* Different alignments for different contexts */
.sidebar lazy-video {
--lv-align: right;
}
You can convert existing video iframes to <lazy-video> by simply changing the tag name.
Standard YouTube iframe:
<iframe
src="https://www.youtube.com/embed/wPr3kws2prM?start=30&rel=0&controls=0"
width="560"
height="315"
title="Till We Have Faces by Silent Planet"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
Converted to <lazy-video> (just change the tag):
<lazy-video
src="https://www.youtube.com/embed/wPr3kws2prM?start=30&rel=0&controls=0"
width="560"
height="315"
title="Till We Have Faces by Silent Planet"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</lazy-video>
<lazy-video> is built with modern web security and privacy best practices:
credentialless attribute. This helps prevent credential leaks and keeps third-party content isolated from your site's cookies and storage.
sandbox attribute is set by default, restricting what the embedded player can do and reducing risk from third-party content.
youtube-nocookie.com domain is used by default, so no tracking cookies are set unless the user interacts with the video.
Note: You can override the sandbox attribute if you need to enable additional features, but the default is designed for maximum safety.
Works in all modern browsers (Chrome, Firefox, Safari, Edge). Uses standard web component APIs. For IE11 or older, use the custom-elements polyfill.
April 3, 2025: The old <lazy-youtube> element is no longer supported. Please update any code to use <lazy-video> instead.