Lazyload

A lightweight JavaScript utility built on top of the IntersectionObserver API to lazily:

  • 🖼️ Load images
  • 🎥 Load videos
  • ⚙️ Execute functions

Only when the target element enters the viewport.

This is a vanilla JavaScript utility. It can be used in React / Next.js and other frameworks as well, also have TypeScript support. Follow the documentation below for more details.

Lazyload defers loading of images and videos, and delays execution of functions on long web pages until they enter the viewport. Resources outside the visible area are not loaded or executed until the user scrolls to them, improving performance and reducing unnecessary network usage. This behavior is the opposite of preloading.

This is a modern, dependency-free vanilla JavaScript utility built on top of the Intersection Observer API. It observes when target elements enter the browser’s viewport and then dynamically loads images, loads videos, or executes functions exactly once. Modern browser APIs and best practices are followed to ensure efficiency, simplicity, and flexibility.

📄 Features

  • Lazy load images and videos
  • Execute functions on viewport entry
  • Configurable viewport, margins, and thresholds
  • Zero dependencies

📦 Installation

bash
npm install @bitlaab/lazyload@latest

Or

bash
yarn add @bitlaab/lazyload@latest
js
import { Lazyload } from "@bitlaab/lazyload";

Or import directly using CDN

Note: Replace the <version> with actual version number.

js
import { Lazyload } from "https://cdn.jsdelivr.net/npm/@bitlaab/lazyload@<version>/dist/index.js";

🏷️ TypeScript Support

The package provides full TypeScript support. You can import the exported types for your own usage:

ts
// From npm / yarn
import { Lazyload, TypeLazyOptions, TypeLazyMedia, TypeLazyExecute } from "@bitlaab/lazyload";

Exported Types

  • TypeLazyOptions: The global configuration object for observer margins and thresholds.
  • TypeLazyMedia: The configuration object for the media() method.
  • TypeLazyExecute: The configuration object for the execute() method.

Example Usage

ts
import { Lazyload, TypeLazyMedia } from "@bitlaab/lazyload";

const lazyload = new Lazyload();

const config: TypeLazyMedia = {
    wrapper: document.querySelector("#item-wrapper"),
    srcTarget: ".lazy-item",
    lazyUrls: ["/image.jpg", "/video.mp4"]
};

lazyload.media(config);

📄 Global Options

Most of the time you don't need to use it, the package internally handle those options.
Only use, when you want manual control.

js
options: {
    root: null,
    loadBefore: 0,
    loadAfter: 0
}
OptionTypeDescription
rootDOM Elementnull
loadBeforenumberMargin before entering viewport
loadAfternumberVisibility ratio (0–1)

Example

js
lazyload.media({
    wrapper: ...,
    srcTarget: "...",
    options: {root: ..., loadBefore: 100, loadAfter: 0}
});

// or

lazyload.execute({
    viewportEntry: ...,
    exeFn: ...,
    options: {root: ..., loadBefore: 0, loadAfter: 0.8}
});

📄 Global Methods

MethodDescription
media()Load images and videos when element will enter into the viewport.
execute()Execute function when element will enter into the viewport.

🖼️ 🎥 media()

It will load images and videos when item will enter into the viewport. the method accept 5 parameters of object:

  • wrapper: (Optional) - The wrapper of the srcTarget. The value of it is DOM Element.
    e.g., wrapper: document.querySelector("#item-wrapper")
    Note: If you don't provide the wrapper it will select all of the elements with the selector of srcTarget from DOM, otherwise it will select only from the wrapper. You can use it on your needs.

  • srcTarget: (Required) - The selector of the elements where the image src will be set. The value of it is CSS Selector.
    e.g., srcTarget: ".lazy-item"

  • lazyUrls: (Optional) - An array of string(image or video url).
    e.g., lazyUrls: [ "/image.jpg", "/video.mp4"]

  • attr: (Optional) - An attribute where image url will be set. If you use this parameter, the image url will be set into this attribute of the srcTarget element, otherwise it will set src attribute with the image url into the srcTarget element by default.
    e.g., attr: "data-xyz".
    result <img data-xyz="/image.jpg" />

  • options: (Optional) - The configuration of the lazyload. Most of the time you don't need to use it, the package internally handle it. Only use, when you want manual control. the value of it is an object with 3 properties and all are optional.
    e.g., options: {root: null, loadBefore: 0, loadAfter: 0}

YOU CAN LOAD MEDIA IN TWO WAYS:

First Example

Setting the media url into an attribute called data-lazy-url

html
<div id="item-wrapper">
    <img class="lazy-item" data-lazy-url="/image.jpg"/>
    <video class="lazy-item" data-lazy-url="/video.mp4"></video>
</div>
js
const lazyload = new Lazyload();

lazyload.media({
    wrapper: document.querySelector("#item-wrapper"),
    srcTarget: ".lazy-item",
});

Second Example

Passing lazyUrls: [](Array of images or videos urls) parameter of the lazyload.media() function.

html
<div id="item-wrapper">
    <img class="lazy-item"/>
    <video class="lazy-item"></video>
</div>
js
const lazyload = new Lazyload();

lazyload.media({
    wrapper: document.querySelector("#item-wrapper"),
    srcTarget: ".lazy-item",
    lazyUrls: ["/image.jpg", "/video.mp4"],
});

⚙️ execute()

It will execute function when viewportEntry element will enter into the viewport. the method accept 3 parameters:

  • viewportEntry: (Required) - The viewportEntry is the element where will be the function call, when the element enter into the viewport the function will be called. The value of it is DOM Element.
    e.g., viewportEntry: document.querySelector("#stats")

  • exeFn: (Required) - The value of this parameter is a function() which will be execute.
    e.g., exeFn: () = {};

  • options: (Optional) - The configuration of the lazyload. Most of the time you don't need to use it, the package internally handle it. Only use, when you want manual control. the value of it is an object with 3 properties and all are optional.
    e.g., options: {root: null, loadBefore: 0, loadAfter: 0}

Example

html
<div id="stats">
    <h1 class="title"></h1>
</div>
js
async function fetchPosts() {
    const url = "https://jsonplaceholder.typicode.com/posts"
    const resp = await fetch(url);
    const arr = await resp.json();

    document.querySelector(".title").textContent = arr[0].title
}

// ---------------------------------------------------------------------

const lazyload = new Lazyload();

lazyload.execute({
    viewportEntry: document.querySelector("#stats"),
    exeFn: fetchPosts,
});

⚛️ React, Next.js, Vue

Because @bitlaab/lazyload is a pure vanilla JavaScript utility, it perfectly integrates with modern frameworks like React, Next.js, and Vue.

  • Next.js (SSR): The package is entirely safe to import in Server-Side Rendered applications. DOM APIs (document, IntersectionObserver) are intentionally never accessed during initialization—only when the .media() or .execute() methods are explicitly invoked on the client side.

  • React: To use the package within React components, make sure to instantiate and invoke it inside a client-side useEffect hook so that it runs after your initial DOM mounts.

  • Vue: In Vue components, you can safely initialize the script inside the onMounted lifecycle hook.

React / Next.js Example

tsx
import { useEffect } from 'react';
import { Lazyload, TypeLazyMedia } from '@bitlaab/lazyload';

export default function MyComponent() {
  useEffect(() => {
    // Guaranteed to run safely exclusively on the client-side
    const lazyload = new Lazyload();

    const config: TypeLazyMedia = {
      srcTarget: ".lazy-item",
      lazyUrls: ["/image.jpg"]
    };

    lazyload.media(config);
  }, []);

  return (
    <div>
      <img className="lazy-item" alt="Lazy Loaded" />
    </div>
  );
}

Vue 3 (Composition API) Example

vue
<script setup lang="ts">
import { onMounted } from 'vue';
import { Lazyload, TypeLazyMedia } from '@bitlaab/lazyload';

onMounted(() => {
  // Guaranteed to run safely entirely on the client-side
  const lazyload = new Lazyload();

  const config: TypeLazyMedia = {
    srcTarget: ".lazy-item",
    lazyUrls: ["/image.jpg"]
  };

  lazyload.media(config);
});
</script>

<template>
  <div>
    <img class="lazy-item" alt="Lazy Loaded" />
  </div>
</template>