# verlok/vanilla-lazyload architecture

> Architecture diagram of the GitHub repository verlok/vanilla-lazyload, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-27). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/verlok/vanilla-lazyload
- Repository: https://github.com/verlok/vanilla-lazyload

## Overview

1. This repository is a client-side JavaScript library, not an application server. Its core product is a `LazyLoad` class that defers loading of images, responsive images, background images, videos, iframes, SVG/object content, and even arbitrary functions until elements are near or inside the viewport.

2. The main runtime boundary is the `src/` library code versus the `dist/` published bundles. `src/` is the authored source, while `dist/esm`, `dist/lazyload.js`, and `dist/lazyload.iife.js` are build outputs for ES modules, UMD, and IIFE consumers. The same architecture is preserved across these bundles.

3. The top-level orchestration lives in `src/lazyload.js`. It is the public API surface that wires together options, element discovery, observer setup, load triggers, lifecycle methods, and bookkeeping. From an architecture diagram perspective, this is the central controller.

4. Element discovery is based on a container + selector model. By default, the library scans descendants of a scrolling container for elements with the `.lazy` class, but both the container and the selector are configurable. This means the library supports the main document as well as nested scrolling panels as separate observation scopes.

5. The viewport detection path is built around `IntersectionObserver`. The `intersectionObserver`, `intersectionHandlers`, and `unobserve` modules appear to split observer setup, per-entry enter/exit handling, and cleanup. The data flow is: discovered elements are observed, intersection events fire, handlers decide whether to enter/exit/load/unobserve, and state is updated accordingly.

6. Actual loading is delegated to type-specific helpers. The `load`, `set`, `forEachSource`, and `tempImage` modules likely handle translating `data-*` attributes into real DOM attributes for different targets: `img`, `picture/source`, `video/source`, `iframe`, `object`, and background-image variants. This is the key content-loading boundary between lazy metadata and browser-recognized resource URLs.

7. The library maintains per-element state and original attributes so it can reverse changes later. `data`, `originalAttributes`, `elementStatus`, `reset`, and `restore` indicate that LazyLoad tracks whether an element is pending, loading, loaded, errored, entered, or exited, and can reset or restore DOM back to its initial state. That makes lifecycle management a first-class concern, especially for SPAs or retry flows.

8. There is explicit support for cancellation and error recovery. `cancelOnExit` handles abort-like behavior when an image leaves the viewport before finishing, while `online` suggests retry logic when network connectivity returns. Together these modules add a resilience layer around the basic “load on enter” behavior.

9. Native-lazy-loading support is a separate branch, not just a flag on the same code path. The `native` and `environment` modules indicate feature detection and a hybrid mode where the library can delegate supported `img`/`iframe`/`video` loading to the browser via `loading="lazy"`, while still handling unsupported cases such as background images through the JS observer path.

10. The library exposes rich callbacks and DOM class hooks as its integration boundary. `callback`, `event`, `class`, and `defaults` suggest that consumers can react to enter/load/error/finish events and style loading states through configurable class names. This is the main way application code hooks into the internal pipeline without touching implementation details.

11. `autoInitialize.js` is the bridge for the “async script” usage model described in the README. It allows the library to self-start from global options and emit an initialization event, which is important for CDN deployments where code runs after the script tag loads instead of via direct construction.

12. The repo includes extensive verification and usage surfaces: unit tests for stateful helpers like load/reset/restore/set/cancel behavior, end-to-end Playwright tests for real browser loading scenarios, and many HTML demos that exercise each feature combination. That makes the architecture easy to validate across browser APIs and edge cases such as dynamic content, multiple containers, placeholders, and error handling.

13. Tooling is centered on packaging and browser compatibility rather than a server runtime: Rollup for bundling, Jest for unit tests, Playwright for browser E2E, and CI workflows in GitHub Actions. There is also a TypeScript declaration file in `typings/`, so the library is distributed with typed consumer support even though the source is plain JavaScript.

Main entry points: [`src/lazyload.js`](https://github.com/verlok/vanilla-lazyload/blob/master/src/lazyload.js) (LazyLoad), [`src/load.js`](https://github.com/verlok/vanilla-lazyload/blob/master/src/load.js) (Load), [`rollup.config.mjs`](https://github.com/verlok/vanilla-lazyload/blob/master/rollup.config.mjs) (Rollup), [`src/intersectionHandlers.js`](https://github.com/verlok/vanilla-lazyload/blob/master/src/intersectionHandlers.js) (Handlers)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_group_source["Source"]
  node_node_lazyload["LazyLoad<br/>public API<br/>[lazyload.js]"]
  node_node_defaults["Defaults<br/>config<br/>[defaults.js]"]
  node_node_dom["DOM scan<br/>discovery<br/>[dom.js]"]
  node_node_io["Observer<br/>viewport"]
  node_node_handlers["Handlers<br/>intersection flow"]
  node_node_unobserve["Unobserve<br/>cleanup<br/>[unobserve.js]"]
  node_node_load["Load<br/>[load.js]"]
  node_node_set["Set attrs<br/>attribute mapping<br/>[set.js]"]
  node_node_sources["Sources<br/>source iteration<br/>[forEachSource.js]"]
  node_node_temp["Temp image<br/>preload helper<br/>[tempImage.js]"]
  node_node_state["State<br/>bookkeeping<br/>[data.js]"]
  node_node_restore["Restore<br/>lifecycle<br/>[restore.js]"]
  node_node_cancel["Cancel exit<br/>resilience<br/>[cancelOnExit.js]"]
  node_node_native["Native mode<br/>feature detect<br/>[native.js]"]
  node_node_online["Online<br/>retry<br/>[online.js]"]
  node_node_auto["Auto init<br/>async startup<br/>[autoInitialize.js]"]
end

subgraph group_group_bundles["Bundles"]
  node_node_esm["ESM bundle<br/>published build<br/>[lazyload.js]"]
  node_node_umd["UMD bundle<br/>published build<br/>[lazyload.js]"]
  node_node_iife["IIFE bundle<br/>published build<br/>[lazyload.iife.js]"]
end

subgraph group_group_quality["Verification"]
  node_node_unit["Unit tests<br/>verification"]
  node_node_e2e["E2E tests<br/>browser verification"]
  node_node_demos["Demos<br/>examples"]
end

subgraph group_group_tooling["Tooling"]
  node_node_typings["Types<br/>declarations<br/>[lazyload.d.ts]"]
  node_node_rollup["Rollup<br/>bundling<br/>[rollup.config.mjs]"]
  node_node_jest["Jest<br/>test runner<br/>[jest.config.cjs]"]
  node_node_playwright["Playwright<br/>browser runner"]
end

node_node_lazyload -->|"reads config"| node_node_defaults
node_node_lazyload -->|"discovers"| node_node_dom
node_node_lazyload -->|"starts observation"| node_node_io
node_node_io -->|"emits entries"| node_node_handlers
node_node_handlers -->|"triggers"| node_node_load
node_node_handlers -->|"cleans up"| node_node_unobserve
node_node_load -->|"maps attrs"| node_node_set
node_node_load -->|"walks sources"| node_node_sources
node_node_load -->|"uses preload"| node_node_temp
node_node_load -->|"updates state"| node_node_state
node_node_state -->|"supports reset"| node_node_restore
node_node_state -->|"tracks inflight"| node_node_cancel
node_node_lazyload -->|"feature branch"| node_node_native
node_node_lazyload -->|"retry path"| node_node_online
node_node_lazyload -->|"bootstraps"| node_node_auto
node_node_lazyload -->|"published as"| node_node_esm
node_node_lazyload -->|"published as"| node_node_umd
node_node_lazyload -->|"published as"| node_node_iife
node_node_rollup -->|"produces"| node_node_esm
node_node_rollup -->|"produces"| node_node_umd
node_node_rollup -->|"produces"| node_node_iife
node_node_jest -->|"runs"| node_node_unit
node_node_playwright -->|"runs"| node_node_e2e
node_node_demos -->|"exercises"| node_node_lazyload
node_node_typings -->|"types API"| node_node_lazyload

click node_node_lazyload "https://github.com/verlok/vanilla-lazyload/blob/master/src/lazyload.js"
click node_node_defaults "https://github.com/verlok/vanilla-lazyload/blob/master/src/defaults.js"
click node_node_dom "https://github.com/verlok/vanilla-lazyload/blob/master/src/dom.js"
click node_node_io "https://github.com/verlok/vanilla-lazyload/blob/master/src/intersectionObserver.js"
click node_node_handlers "https://github.com/verlok/vanilla-lazyload/blob/master/src/intersectionHandlers.js"
click node_node_unobserve "https://github.com/verlok/vanilla-lazyload/blob/master/src/unobserve.js"
click node_node_load "https://github.com/verlok/vanilla-lazyload/blob/master/src/load.js"
click node_node_set "https://github.com/verlok/vanilla-lazyload/blob/master/src/set.js"
click node_node_sources "https://github.com/verlok/vanilla-lazyload/blob/master/src/forEachSource.js"
click node_node_temp "https://github.com/verlok/vanilla-lazyload/blob/master/src/tempImage.js"
click node_node_state "https://github.com/verlok/vanilla-lazyload/blob/master/src/data.js"
click node_node_restore "https://github.com/verlok/vanilla-lazyload/blob/master/src/restore.js"
click node_node_cancel "https://github.com/verlok/vanilla-lazyload/blob/master/src/cancelOnExit.js"
click node_node_native "https://github.com/verlok/vanilla-lazyload/blob/master/src/native.js"
click node_node_online "https://github.com/verlok/vanilla-lazyload/blob/master/src/online.js"
click node_node_auto "https://github.com/verlok/vanilla-lazyload/blob/master/src/autoInitialize.js"
click node_node_esm "https://github.com/verlok/vanilla-lazyload/blob/master/dist/esm/lazyload.js"
click node_node_umd "https://github.com/verlok/vanilla-lazyload/blob/master/dist/lazyload.js"
click node_node_iife "https://github.com/verlok/vanilla-lazyload/blob/master/dist/lazyload.iife.js"
click node_node_unit "https://github.com/verlok/vanilla-lazyload/tree/master/tests/unit"
click node_node_e2e "https://github.com/verlok/vanilla-lazyload/tree/master/tests/e2e"
click node_node_demos "https://github.com/verlok/vanilla-lazyload/tree/master/demos"
click node_node_typings "https://github.com/verlok/vanilla-lazyload/blob/master/typings/lazyload.d.ts"
click node_node_rollup "https://github.com/verlok/vanilla-lazyload/blob/master/rollup.config.mjs"
click node_node_jest "https://github.com/verlok/vanilla-lazyload/blob/master/jest.config.cjs"
click node_node_playwright "https://github.com/verlok/vanilla-lazyload/blob/master/playwright.config.js"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_node_lazyload,node_node_defaults,node_node_dom,node_node_io,node_node_handlers,node_node_unobserve,node_node_load,node_node_set,node_node_sources,node_node_temp,node_node_state,node_node_restore,node_node_cancel,node_node_native,node_node_online,node_node_auto toneBlue
class node_node_esm,node_node_umd,node_node_iife toneAmber
class node_node_unit,node_node_e2e,node_node_demos toneMint
class node_node_typings,node_node_rollup,node_node_jest,node_node_playwright toneRose
```

## Components

### Source

Authored library modules in src/.

- [LazyLoad](https://github.com/verlok/vanilla-lazyload/blob/master/src/lazyload.js): public API · Top-level controller that wires options, discovery, observation, loading, lifecycle, and cleanup. · `src/lazyload.js`
- [Defaults](https://github.com/verlok/vanilla-lazyload/blob/master/src/defaults.js): config · Default options and integration hooks for consumers. · `src/defaults.js`
- [DOM scan](https://github.com/verlok/vanilla-lazyload/blob/master/src/dom.js): discovery · Container and selector-based element discovery inside scroll scopes. · `src/dom.js`
- [Observer](https://github.com/verlok/vanilla-lazyload/blob/master/src/intersectionObserver.js): viewport · IntersectionObserver setup and element observation lifecycle. · `src/intersectionObserver.js`
- [Handlers](https://github.com/verlok/vanilla-lazyload/blob/master/src/intersectionHandlers.js): intersection flow · Per-entry enter/exit decisions and load triggering. · `src/intersectionHandlers.js`
- [Unobserve](https://github.com/verlok/vanilla-lazyload/blob/master/src/unobserve.js): cleanup · Removes observed elements and finalizes observer cleanup. · `src/unobserve.js`
- [Load](https://github.com/verlok/vanilla-lazyload/blob/master/src/load.js): loader · Delegates resource loading for lazy elements and variants. · `src/load.js`
- [Set attrs](https://github.com/verlok/vanilla-lazyload/blob/master/src/set.js): attribute mapping · Translates lazy data attributes into live DOM attributes. · `src/set.js`
- [Sources](https://github.com/verlok/vanilla-lazyload/blob/master/src/forEachSource.js): source iteration · Iterates over picture/source descendants during load preparation. · `src/forEachSource.js`
- [Temp image](https://github.com/verlok/vanilla-lazyload/blob/master/src/tempImage.js): preload helper · Temporary image handling used during staged loading and detection. · `src/tempImage.js`
- [State](https://github.com/verlok/vanilla-lazyload/blob/master/src/data.js): bookkeeping · Per-element data, status, counters, and original attributes. · `src/data.js`
- [Restore](https://github.com/verlok/vanilla-lazyload/blob/master/src/restore.js): lifecycle · Resets or restores elements back to their initial DOM state. · `src/restore.js`
- [Cancel exit](https://github.com/verlok/vanilla-lazyload/blob/master/src/cancelOnExit.js): resilience · Aborts in-flight loading when elements leave the viewport. · `src/cancelOnExit.js`
- [Native mode](https://github.com/verlok/vanilla-lazyload/blob/master/src/native.js): feature detect · Feature detection and hybrid delegation to browser native lazy-loading. · `src/native.js`
- [Online](https://github.com/verlok/vanilla-lazyload/blob/master/src/online.js): retry · Network-recovery path for retrying failed or pending loads. · `src/online.js`
- [Auto init](https://github.com/verlok/vanilla-lazyload/blob/master/src/autoInitialize.js): async startup · Self-start bridge for script-tag and global-options usage. · `src/autoInitialize.js`

### Bundles

Published build outputs in dist/.

- [ESM bundle](https://github.com/verlok/vanilla-lazyload/blob/master/dist/esm/lazyload.js): published build · ES module distribution bundle. · `dist/esm/lazyload.js`
- [UMD bundle](https://github.com/verlok/vanilla-lazyload/blob/master/dist/lazyload.js): published build · Browser-friendly bundle for module and script consumers. · `dist/lazyload.js`
- [IIFE bundle](https://github.com/verlok/vanilla-lazyload/blob/master/dist/lazyload.iife.js): published build · Self-executing bundle for direct browser inclusion. · `dist/lazyload.iife.js`

### Verification

Tests and browser demos that exercise the library.

- [Unit tests](https://github.com/verlok/vanilla-lazyload/tree/master/tests/unit): verification · Stateful helper coverage for loading, restoring, and cancellation behavior. · `tests/unit`
- [E2E tests](https://github.com/verlok/vanilla-lazyload/tree/master/tests/e2e): browser verification · Playwright scenarios for real browser lazy-loading flows. · `tests/e2e`
- [Demos](https://github.com/verlok/vanilla-lazyload/tree/master/demos): examples · Interactive HTML scenarios covering images, media, backgrounds, containers, and async use. · `demos`

### Tooling

Packaging, typing, and repository infrastructure.

- [Types](https://github.com/verlok/vanilla-lazyload/blob/master/typings/lazyload.d.ts): declarations · TypeScript declarations for consumer integration. · `typings/lazyload.d.ts`
- [Rollup](https://github.com/verlok/vanilla-lazyload/blob/master/rollup.config.mjs): bundling · Build pipeline that produces the distributed bundles. · `rollup.config.mjs`
- [Jest](https://github.com/verlok/vanilla-lazyload/blob/master/jest.config.cjs): test runner · Unit test runner configuration. · `jest.config.cjs`
- [Playwright](https://github.com/verlok/vanilla-lazyload/blob/master/playwright.config.js): browser runner · End-to-end browser test configuration. · `playwright.config.js`

## Connections

- LazyLoad → Defaults: reads config · Initializes runtime behavior from default and consumer options.
- LazyLoad → DOM scan: discovers · Finds lazy elements within the configured container and selector.
- LazyLoad → Observer: starts observation · Sets up viewport observation for discovered elements.
- Observer → Handlers: emits entries · Feeds intersection events into enter/exit handling.
- Handlers → Load: triggers · Loads elements that become eligible in the viewport.
- Handlers → Unobserve: cleans up · Stops observing elements after completion or exit handling.
- Load → Set attrs: maps attrs · Converts data attributes into live resource attributes.
- Load → Sources: walks sources · Processes picture/source descendants during loading.
- Load → Temp image: uses preload · Leans on temporary image handling for staged loading.
- Load → State: updates state · Records loading progress and per-element bookkeeping.
- State → Restore: supports reset · Preserves originals so DOM can be restored later.
- State → Cancel exit: tracks inflight · Provides state for cancel-on-exit behavior.
- LazyLoad → Native mode: feature branch · Delegates supported elements to browser native lazy-loading when available.
- LazyLoad → Online: retry path · Reconnects loading logic after network recovery.
- LazyLoad → Auto init: bootstraps · Supports async script initialization from global options.
- LazyLoad → ESM bundle: published as · Source library is bundled into the ES module distribution.
- LazyLoad → UMD bundle: published as · Source library is bundled into the UMD distribution.
- LazyLoad → IIFE bundle: published as · Source library is bundled into the IIFE distribution.
- Rollup → ESM bundle: produces · Rollup generates the ES module build.
- Rollup → UMD bundle: produces · Rollup generates the browser UMD build.
- Rollup → IIFE bundle: produces · Rollup generates the IIFE build.
- Jest → Unit tests: runs · Jest executes unit-level helper tests.
- Playwright → E2E tests: runs · Playwright executes browser end-to-end scenarios.
- Demos → LazyLoad: exercises · Demo pages showcase the runtime API and feature combinations.
- Types → LazyLoad: types API · Declaration file describes the public JavaScript API.
