v-lazy-image: lazy loading for Vue with a progressive placeholder
Lazy load images using Intersection Observer, apply progressive rendering and css animations.
At a glance
- What is it?
- v-lazy-image is a Vue component that loads an image only when it intersects the viewport, using the Intersection Observer API, with support for responsive images, picture tags and a progressive placeholder. It suits a Vue developer with image-heavy pages, and it needs a polyfill on browsers without Intersection Observer.
- Who is it for?
- Adopt v-lazy-image if your Vue pages are image heavy, because the component defers the request to the moment the image is actually needed and lets you pair it with responsive images and a low quality placeholder. Do not adopt it as a replacement for the native loading attribute, and remember it is a Vue component rather than a framework agnostic helper.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 17 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One component and one required prop
v-lazy-image is a Vue.js component that lazy loads an image automatically when it enters the viewport, using the Intersection Observer API. That is the whole idea, and the whole installation story is one package.
npm install v-lazy-imageThe install comes with a warning that matters for a library that touches a browser API. You will need to install the w3c Intersection Observer polyfill in case you are targeting a browser which does not support it. The README points at the polyfill repository, and the requirement is a direct consequence of the design, since the component is built on that API rather than on scroll listeners.
Using it in Vue 3 is the ordinary import-and-use path:
<script setup>
import VLazyImage from "v-lazy-image";
</script>And for Vue 2 the import path differs, because the component has to be registered rather than imported into a setup block:
import VLazyImage from "v-lazy-image/v2";
export default {
components: {
VLazyImage
}
};The usage itself is one tag with a src:
<template>
<v-lazy-image src="http://lorempixel.com/400/200/" />
</template>That easy, the image loads as soon as it enters the viewport. The demo linked from the README shows it running and explains how to use Chrome DevTools to check how it is loaded, which is the detail a developer should care about, since a lazy loading component either works or quietly does nothing and the network panel tells you which.
The props table, and the placeholder that does the real work
The API is small, and the README states the general rule first: aside from the listed API you can pass any img attribute, such as alt, and they will be added to the rendered img tag.
That single sentence is worth more than the prop table, because it means the component is not a closed set. Anything the browser understands on an img element works, and the component adds its own five props on top.
Those five are src, which is required and is the image to lazy load when it intersects with the viewport. src-placeholder, a string defaulting to a single space, and if it is defined it is shown until the src image is loaded, which is useful for progressive image loading. srcset, for images at different resolutions. intersection-options, an object defaulting to an empty function result, which is the Intersection Observer options object passed through. And use-picture, a boolean defaulting to false, which wraps the img in a picture tag.
The src-placeholder is the prop that makes the component more than a lazy loader. A plain lazy loader shows an empty box until the image arrives, which is fine for a small icon and bad for a large photo, because the layout jumps when the content finally appears. With a placeholder the box is already filled, and the swap is a crossfade or a blur-up rather than a jump. The README links a demo of the progressive blur technique using SVG for exactly that purpose.
The use-picture prop is the other one that matters for real pages, because a picture tag is what lets the browser pick the right source from a srcset for the device it is running on, and the use-picture option is how the component cooperates with that standard rather than working around it.
The three events, and why they are not the same as the DOM ones
The component emits three events, and the naming is worth reading carefully because two of them shadow native DOM event names.
The intersect event is triggered when the image intersects with the viewport. The load event is triggered when the lazy image defined in src is loaded. And the error event is triggered when the lazy image defined in src fails to load.
The intersect event is the interesting one. A lazy loading component that only tells you the image arrived is half a component, because what you often want is to know the image is about to arrive, so you can start your own work, prefetch the next thing, or fire analytics on a viewable impression rather than a load event. The load event, on a lazy loaded image, fires when the browser has finished fetching it, which is not the same as when the user could see it, and treating load as an impression is a common mistake.
The error event is the one that pays for itself. A lazy loaded image is, by definition, fetched later and in a different context than the initial page load, so a failure is more likely and less visible. Without an error event, a broken image in a long list is a blank space that nobody notices until a user complains. With one, you can render a fallback.
The README also notes that you can pass any img attribute and that they will be added to the rendered img tag, so an onerror handler on the underlying element is available as well, though the component's own event is the more direct route.
The performance story is progressive, and the README says so
The Achieving Max Performance section is structured as a ladder, and the wording is worth noting because it is more modest than the feature list might suggest.
The first rung is that just by using v-lazy-image you will have a performance gain, since the image will be loaded when it is going to be seen. The second is that you can go to the next level and squeeze your web's performance if you use the next techniques. The component is the baseline, not the whole answer.
The second rung has two parts. Responsive images come first, where v-lazy-image allows you to use the web standard srcset attribute on images and the picture tag, with a linked article showing how simple it is and a demo to see it in action. Then progressive image loading, described as a technique used by platforms like Spotify, Netflix or Medium to improve perceived performance and thus the user experience, applied through the src-placeholder attribute and shown with a CSS animation in a demo.
The perceived performance point is the substantive one, and it is a different claim from a raw load time reduction. An image that appears blurred and sharpens is perceived as faster than one that appears from nothing, even if the bytes take the same time. That is what the placeholder mechanism buys.
The README is also direct about when to reach for something else. It warns the reader to check the practical examples and demos if they are creating a real-world or enterprise project and to see how to achieve max performance using responsive images and progressive image loading. So the author is pointing enterprise users at the article series rather than at the component alone.
A workspace with two playgrounds, and a package that publishes both Vue versions
The repository layout shows how the project is organised, and the top level is a pnpm workspace with a.gitignore, a LICENSE, a README, a pnpm-lock.yaml, a pnpm-workspace.yaml, and three directories: v-lazy-image/, playground-vue2/ and playground-vue3/.
The two playgrounds are the clearest signal of the project's priorities. A component that supports both Vue 2 and Vue 3 needs both to be exercised, and separate playground applications are the normal way to do that in a monorepo.
The root package.json is a workspace root rather than a published package. It is named v-lazy-image-ws, version 0.0.0, with the workspaces listed as v-lazy-image, playground-vue2 and playground-vue3, an empty scripts object, and a single devDependency of pnpm at 6.12.1 or newer. The workspace suffix on the name is a common convention, marking the root as a container rather than something you install.
The component itself is published on npm as v-lazy-image, with version and monthly download badges in the README header, and the topic tags are javascript, lazy-loading, vue and web-performance. The licence is MIT, the primary language is JavaScript, and the last push to the master branch was on 2026-10-06 with no GitHub releases.
The demo links in the README are more numerous than the feature list, and they cover the ground: a simple demo, a responsive images demo and a progressive image loading demo with animations, both by the same contributor, and a performant progressive blur using SVG. Four demos for one component is a sign the author expects the component to be combined with techniques rather than used on its own.
Editorial conclusion
Adopt v-lazy-image if your Vue pages are image heavy, because the component defers the request to the moment the image is actually needed and lets you pair it with responsive images and a low quality placeholder. Do not adopt it as a replacement for the native loading attribute, and remember it is a Vue component rather than a framework agnostic helper. Before you use it, decide which Vue version you are on, since Vue 2 imports from v-lazy-image/v2 and Vue 3 from the package root, and install the Intersection Observer polyfill if any browser in your support matrix lacks it.
Frequently asked questions
How do I install and use v-lazy-image in Vue?
Run `npm install v-lazy-image`, then import it. In Vue 3 use `import VLazyImage from "v-lazy-image";` and in Vue 2 import it from `v-lazy-image/v2` and register it in components. You must pass a src prop, and the image loads as soon as it enters the viewport.
Which props does v-lazy-image accept?
src is required and is the image to load on intersection. src-placeholder, defaulting to a single space, is shown until the src image loads and is used for progressive image loading. srcset gives images for different resolutions, intersection-options passes the Intersection Observer options object, and use-picture wraps the img in a picture tag. Any other img attribute such as alt is added to the rendered tag.
Which events does the v-lazy-image component emit?
Three: intersect when the image intersects with the viewport, load when the lazy image defined in src is loaded, and error when that image fails to load. The intersect event is what tells you the image is about to become visible, which is not the same as the load event.
Does v-lazy-image need a polyfill?
Potentially. The component is built on the Intersection Observer API, and the README warns that you need to install the w3c Intersection Observer polyfill if you are targeting a browser which does not support it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/alexjoverm-v-lazy-image)Was this useful?