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

lazyvid

v0.1.3

Published

Lightweight lazy-loading video component with IntersectionObserver

Readme

lazyvid

Lazy-load <video> sources with predictable behavior.

lazyvid renders an empty <video> element and injects <source> tags only when the element enters the viewport.
Until then — nothing downloads.

It relies on IntersectionObserver for visibility tracking and lets the browser handle native format selection (webm, mp4, etc).

~2 KB (ESM bundle). Zero dependencies. Full TypeScript support.

<LazyVideo
  sources={[
    { src: "/hero.webm", type: "video/webm" },
    { src: "/hero.mp4", type: "video/mp4" },
  ]}
  muted
  autoPlay
  loop
/>

Single component. No additional setup.

Why?

Browsers start fetching <video> sources as soon as <source> elements are present in the DOM. On media-heavy pages, this means unnecessary bandwidth usage and background CPU activity — even if the user never scrolls to the video.

lazyvid avoids that by delaying source injection until the element becomes visible (with configurable preload offset).

  • No user-agent checks.
  • No format guessing.

The browser still decides which source to play.

What it does

  • Renders an empty <video>
  • Observes it with IntersectionObserver
  • Injects <source> elements when it enters the viewport
  • Optionally pauses playback when it leaves

That’s it.

Install

npm install lazyvid

Requires React 18 or 19.

Usage

Basic — just lazy load it

import { LazyVideo } from "lazyvid";

<LazyVideo
  sources={[
    { src: "/promo.webm", type: "video/webm" },
    { src: "/promo.mp4", type: "video/mp4" },
  ]}
  poster="/promo-poster.jpg"
  controls
/>;

Sources are listed in priority order. The browser takes the first format it supports — put your lightest format first.

Background video that pauses off-screen

<LazyVideo
  sources={[
    { src: "/bg.webm", type: "video/webm" },
    { src: "/bg.mp4", type: "video/mp4" },
  ]}
  poster="/bg-poster.jpg"
  autoPlay
  muted
  loop
  pauseOnLeave
  style={{ width: "100%", objectFit: "cover" }}
/>

When the user scrolls away — video pauses. Scrolls back — resumes. No wasted CPU on invisible playback.

onLoaded — trigger actions when video enters viewport

onLoaded fires once, when the video enters the viewport, <source> elements are injected, and loading starts. It does not fire when loading is complete — use onCanPlay for that.

This allows you to build logic on top of it, for example:

  • start animations or UI effects
  • show captions or overlays
  • preload next section, next video, or related data
  • track engagement: the user reached this video
<div style={{ position: "relative" }}>
  <LazyVideo
    sources={[{ src: "/intro.mp4", type: "video/mp4" }]}
    poster="/intro-thumb.jpg"
    controls
    onLoaded={() => {
      console.log("Video entered viewport, loading started!");
      // trigger animations, preload next content, track engagement...
    }}
  />
</div>

Start loading earlier

By default, loading starts 200px before the video is visible. Want more buffer?

<LazyVideo
  sources={[{ src: "/hero.mp4", type: "video/mp4" }]}
  rootMargin="500px"
  muted
  autoPlay
/>

Props

| Prop | Type | Default | Description | | -------------- | --------------------- | --------- | ----------------------------------------------- | | sources | VideoSource[] | required | { src, type } objects in priority order | | threshold | number | 0 | How much of the element should be visible (0–1) | | rootMargin | string | "200px" | Start loading before the element is in view | | pauseOnLeave | boolean | false | Pause when out of viewport, resume when back | | onLoaded | () => void | — | Fires when sources are injected | | ...rest | VideoHTMLAttributes | — | Any native <video> attribute works |

How it works

  1. Renders an empty <video> — nothing downloads
  2. IntersectionObserver watches it (with rootMargin for early preloading)
  3. Element enters viewport → <source> tags injected → browser picks best format → loading starts
  4. Observer disconnects (one-time job)
  5. If pauseOnLeave is on, a second observer manages play/pause on scroll

Types

import { LazyVideo, type VideoSource } from "lazyvid";

License

MIT