
unlazy is a framework-agnostic, SEO-friendly lazy loading library that leverages native browser APIs to display blurry image placeholders until the original images fully load.
The library works with the standard loading=”lazy” attribute and supports both BlurHash & ThumbHash string decoding for placeholder generation.
Features:
- Automatic Sizes Calculation: Computes the
sizesattribute based on the actual image display width. - SEO Bot Detection: Detects search engine crawlers via user agent analysis and scroll event support. Full-quality images load immediately for bots.
- Picture Element Support: Works with
<picture>elements for art direction and format selection. - Framework Agnostic: Integrates with React, Vue, vanilla JavaScript, or any framework.
Use Cases:
- E-commerce Product Galleries: Load hundreds of product images efficiently while maintaining visual quality perception. Blurry placeholders prevent layout shift and provide instant visual feedback.
- Content-Heavy Blogs: Improve Time to Interactive on article pages with many inline images. Search engines still index full-quality images correctly.
- Portfolio Websites: Display high-resolution photography with smooth loading transitions. Hash-based placeholders maintain color accuracy during load.
- Progressive Web Applications: Reduce initial bundle size by deferring image loads until viewport entry.
How to use it:
1. Install and import the unlazy.
# Yarn $ yarn add unlazy # NPM $ npm install unlazy # PNPM $ pnpm install unlazy
import { lazyLoad } from 'unlazy';// Initialize lazy loading for all images with loading="lazy" // This returns a cleanup function you can call later const cleanup = lazyLoad();
<!-- CDN Usage Without Build Tools --> <script src="https://unpkg.com/unlazy" defer init></script>
2. Add the blurry placeholder to the src attribute.
<img loading="lazy" src="data:image/svg+xml, ..." >
3. Specify the path to the original image(s) using the data-srcset attribute.
<img loading="lazy" src="data:image/svg+xml, ..." data-srcset="1x.png 1024w, 2x.png 2048w" data-sizes="auto" width="1024" height="768" >
4. It also works with the picture tag.
<picture>
<source
loading="lazy"
src="data:image/svg+xml, ..."
data-srcset="original.jpg"
media="(min-width: 800px)"
>
</picture>5. Use BlurHash or ThumbHash to generate blurry placeholders.
<img data-srcset="https://source.unsplash.com/EBtfyalTU50/2400x1600" data-blurhash="LKO2:N%2Tw=w]~RBVZRi};RPxuwH" loading="lazy" data-sizes="auto" width="1024" height="768" style="aspect-ratio: 4/3" alt="Image with blurry placeholder" >
<img data-src="image.jpg" data-thumbhash="1QcSHQRnh493V4dIh4eXh1h4kJUI" >
6. Available options.
hash(boolean | string): Controls placeholder generation. Set totrueto auto-detect from data attributes,falseto disable, or pass a specific hash string.hashType(‘blurhash’ | ‘thumbhash’): Specifies the algorithm used for decoding. The default is'blurhash'.placeholderSize(number): Defines the size of the longer edge for BlurHash decoding. The default is32.updateSizesOnResize(boolean): Updates thesizesattribute when the window resizes. The default isfalse.onImageLoad(function): A callback that runs when an image loads successfully.onImageError(function): A callback that runs when an image fails to load.
const cleanup = lazyLoad({
// options here
});7. API methods.
// This calculates the `sizes` attribute without triggering the lazy load logic. It is useful if you handle loading separately but want the responsive sizing utility.
import { autoSizes } from 'unlazy';
// Calculate sizes for all elements with data-sizes="auto"
autoSizes();
// Calculate for a specific list of elements
const images = document.querySelectorAll('.gallery img');
autoSizes(images);// This forces an image to load immediately. It bypasses the viewport check.
import { triggerLoad } from 'unlazy';
const priorityImage = document.querySelector('#must-load-now');
// Force the image to load and swap attributes
triggerLoad(priorityImage);8. Events & callbacks.
lazyLoad('img[loading="lazy"]', {
// Triggered when the high-res image finishes loading
onImageLoad: (image) => {
console.log('Image loaded:', image.src);
image.classList.add('fade-in');
},
// Triggered if the image fails to retrieve
onImageError: (image, error) => {
console.error('Load failed:', image, error);
image.src = 'fallback-placeholder.png';
}
});Alternatives:
- Lazysizes: A robust, highly popular lazy loader.
- Lozad.js: A highly performant library using IntersectionObserver.
FAQs:
Q: Does using Unlazy affect SEO negatively?
A: No. Unlazy includes specific logic to detect bots and crawlers. When a bot is detected, the library immediately loads the full image references. This allows search engines to index your content correctly.
Q: What is the difference between BlurHash and ThumbHash?
A: ThumbHash is a newer format. It encodes more detail, supports alpha channels (transparency), and generally produces smaller hash strings than BlurHash. We recommend ThumbHash for modern applications.
Q: Can I use this with React, Vue, or Svelte?
A: Yes. Unlazy is framework-agnostic. You can use it inside useEffect (React) or onMounted (Vue) hooks. It operates directly on the DOM elements regardless of how they were rendered.
Q: Why do I need explicit width and height attributes?
A: Explicit dimensions prevent Cumulative Layout Shift (CLS). Furthermore, Unlazy uses these dimensions to determine the decoding ratio for BlurHash. If you omit them, the library might decode the hash at a larger size. This causes performance delays.
Changelog:
v1.1.0 (01/28/2026)
- Added onImageError callback
- Fixed: Skip on image load callback if nothing to load
- Rename loadImage to triggerLoad
v1.0.0 (10/17/2025)
- Bugfixes
v0.12.x (03/26/2025)
- Bugfixes
- Allow overwriting loading prop
v0.11.5/6/7/8 (11/12/2024)
- Bugfixes
- Use current date for placeholder id as well
- Outsource check for descendant of picture
v0.11.4 (11/08/2024)
- Bugfixes
v0.11.3 (04/04/2024)
- Bugfixes
- Move from Buffer to Uint8Array
v0.11.2 (03/11/2024)
- Bugfixes
v0.11.1 (02/19/2024)
- nuxt: Emit error event
- vue: Emit error event
v0.11.0 (02/18/2024)
- Ensure image pre load event is called once
- Bugfixes
v0.10.5 (02/18/2024)
- Update sources to prevent duplicate downloads
v0.10.4 (01/07/2024)
- vue,nuxt: loaded event when image is loade
v0.10.3 (01/03/2024)
- Support Svelte 4
v0.10.2 (11/06/2023)
- Rename __ENABLE_HASH_DECODING__ to __UNLAZY_HASH_DECODING__
v0.10.1 (09/12/2023)
- Bugfixed
- Improved performance
v0.10.0 (09/11/2023)
- Bugfixes
v0.9.5 (09/09/2023)
- Don’t expose internal updateSizesAttribute
- core: Respect image sizes if present
- core: Add cleanup handler only if resize handler initiated
- nuxt: Use autoSizes to calculate sizes for preloading
- vue: Use autoSizes to calculate sizes for preloading
v0.9.4 (09/07/2023)
- Expose updateSizesAttribute
- Bugfixes
v0.9.3 (09/01/2023)
- Update
v0.9.1 (07/19/2023)
- Update sizes attribute on image resize with new value
v0.9.1 (07/07/2023)
- vue: Add preload prop
v0.9.0 (06/20/2023)
- Bugfix & Update
v0.8.9 (05/08/2023)
- Bugfixes
v0.8.8 (04/25/2023)
- Bugfixes
v0.8.4 (04/25/2023)
- Bugfixes
- components: src an srcSet props for lazy loading
v0.8.1 (04/25/2023)
- Remove immediate option
- nuxt: lazyLoad prop for data saving mode etc.
- added Svelte component
v0.7.6 (04/24/2023)
- components: immediate prop
- bugfixes
v0.7.0 (04/22/2023)
- ThumbHash support







