Vue.js plugin for lazy-loading images and components with directives, components, and programmatic APIs
—
Vue directives for declarative lazy loading of images and background images in templates.
Main directive for lazy loading images and background images with automatic viewport detection.
/**
* v-lazy directive for lazy loading images
* Supports both img src and background-image loading
*/
interface LazyDirective {
/** Initialize lazy loading when element is mounted */
beforeMount(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void;
/** Handle updates to lazy loading configuration */
beforeUpdate(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void;
/** Trigger lazy load check after updates */
updated(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void;
/** Clean up lazy loading when element is unmounted */
unmounted(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void;
}
/**
* Value types accepted by v-lazy directive
*/
type LazyDirectiveValue = string | VueLazyloadImageOptions;
interface VueLazyloadImageOptions {
/** Image source URL */
src: string;
/** Error fallback image URL */
error?: string;
/** Loading placeholder image URL */
loading?: string;
/** Maximum loading attempts */
attempt?: number;
}Usage Examples:
<template>
<!-- Basic image lazy loading -->
<img v-lazy="imageUrl" alt="Lazy loaded image">
<!-- Background image lazy loading -->
<div v-lazy:background-image="backgroundUrl" class="hero-section"></div>
<!-- Custom container scrolling -->
<div ref="scrollContainer" class="custom-scroll">
<img v-lazy.container="imageUrl" alt="Image in custom container">
</div>
<!-- Object configuration with custom placeholders -->
<img v-lazy="{
src: '/high-res-image.jpg',
loading: '/loading-spinner.gif',
error: '/error-placeholder.png',
attempt: 5
}" alt="High resolution image">
<!-- Srcset support -->
<img
v-lazy="'/image-400.jpg'"
data-srcset="/image-400.jpg 400w, /image-800.jpg 800w, /image-1200.jpg 1200w"
sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
alt="Responsive image">
</template>
<script setup>
import { ref } from "vue";
const imageUrl = ref("/path/to/image.jpg");
const backgroundUrl = ref("/path/to/background.jpg");
</script>Directive for lazy loading multiple images within a container element.
/**
* v-lazy-container directive for batch lazy loading
* Automatically finds and lazy loads images within a container
*/
interface LazyContainerDirective {
/** Initialize container lazy loading */
beforeMount(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void;
/** Update container configuration */
updated(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void;
/** Clean up container lazy loading */
unmounted(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void;
}
/**
* Configuration for v-lazy-container directive
*/
interface LazyContainerOptions {
/** CSS selector for images to lazy load (default: 'img') */
selector?: string;
/** Default error image for all images in container */
error?: string;
/** Default loading image for all images in container */
loading?: string;
}Usage Examples:
<template>
<!-- Basic container lazy loading -->
<div v-lazy-container="{ selector: 'img' }">
<img data-src="/image1.jpg" alt="Image 1">
<img data-src="/image2.jpg" alt="Image 2">
<img data-src="/image3.jpg" alt="Image 3">
</div>
<!-- Container with custom error and loading images -->
<div v-lazy-container="{
selector: 'img',
error: '/error.png',
loading: '/loading.gif'
}">
<img data-src="/photo1.jpg" alt="Photo 1">
<img data-src="/photo2.jpg" alt="Photo 2">
</div>
<!-- Individual image overrides -->
<div v-lazy-container="{ selector: 'img' }">
<img data-src="/image1.jpg" data-error="/custom-error.png" alt="Image 1">
<img data-src="/image2.jpg" data-loading="/custom-loading.gif" alt="Image 2">
<img data-src="/image3.jpg" alt="Image 3">
</div>
<!-- Custom selector for different elements -->
<div v-lazy-container="{ selector: '.lazy-bg' }">
<div class="lazy-bg" data-src="/bg1.jpg">Content 1</div>
<div class="lazy-bg" data-src="/bg2.jpg">Content 2</div>
</div>
</template>Modifiers available for fine-tuning directive behavior.
/**
* Supported modifiers for v-lazy directive
*/
interface LazyDirectiveModifiers {
/** Use custom scrollable container instead of window */
[containerRef: string]: boolean;
}Usage Examples:
<template>
<div ref="customContainer" class="scrollable-area">
<!-- Image will use customContainer as scroll parent -->
<img v-lazy.customContainer="imageUrl" alt="Image in custom container">
</div>
<!-- Multiple images with same container -->
<div ref="gallery" class="image-gallery">
<img v-lazy.gallery="image1" alt="Gallery Image 1">
<img v-lazy.gallery="image2" alt="Gallery Image 2">
<img v-lazy.gallery="image3" alt="Gallery Image 3">
</div>
</template>
<script setup>
import { ref } from "vue";
const customContainer = ref();
const gallery = ref();
const imageUrl = ref("/image.jpg");
const image1 = ref("/gallery1.jpg");
const image2 = ref("/gallery2.jpg");
const image3 = ref("/gallery3.jpg");
</script>CSS classes automatically added to elements to indicate loading state.
/**
* CSS lazy attribute values added to elements
*/
type LazyState = "loading" | "loaded" | "error";Usage Examples:
<template>
<img v-lazy="imageUrl" alt="Lazy image" class="lazy-image">
</template>
<style>
/* Style based on loading state */
.lazy-image[lazy="loading"] {
opacity: 0.5;
background: url('/loading-spinner.gif') center no-repeat;
}
.lazy-image[lazy="loaded"] {
opacity: 1;
transition: opacity 0.3s ease;
}
.lazy-image[lazy="error"] {
opacity: 0.8;
background: url('/error-icon.png') center no-repeat;
}
/* Background image states */
.hero-section[lazy="loading"] {
background-image: url('/loading-pattern.png');
}
.hero-section[lazy="loaded"] {
transition: background-image 0.5s ease;
}
.hero-section[lazy="error"] {
background-image: url('/fallback-bg.jpg');
}
</style>Complex scenarios and integration patterns.
Dynamic Image Sources:
<template>
<!-- Reactive image source -->
<img v-lazy="computedImageUrl" alt="Dynamic image">
<!-- Conditional lazy loading -->
<img v-if="shouldLazyLoad" v-lazy="imageUrl" alt="Conditional lazy image">
<img v-else :src="imageUrl" alt="Direct load image">
</template>
<script setup>
import { computed, ref } from "vue";
const baseUrl = ref("/images/");
const imageName = ref("photo.jpg");
const devicePixelRatio = ref(window.devicePixelRatio || 1);
const computedImageUrl = computed(() => {
const suffix = devicePixelRatio.value > 1 ? "@2x" : "";
return `${baseUrl.value}${imageName.value}${suffix}`;
});
const shouldLazyLoad = computed(() => {
// Only lazy load on mobile or slow connections
return window.innerWidth < 768 || navigator.connection?.effectiveType === "slow-2g";
});
</script>Integration with Vue Transitions:
<template>
<transition name="fade" appear>
<img v-lazy="imageUrl" @load="onImageLoad" alt="Transitioning image">
</transition>
</template>
<script setup>
const onImageLoad = () => {
console.log("Image loaded and transition completed");
};
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>Install with Tessl CLI
npx tessl i tessl/npm-vue-lazyload