Lightweight (~1.4KB gzipped) React lazy loading library with responsive images, blurhash placeholders, and TypeScript support. Simple, fast, and feature-complete.
npm install react-lzy-img




Lightweight React lazy loading library with responsive images, blurhash placeholders, and full TypeScript support. ~1.4KB gzipped.
View full documentation and live examples →
- Lazy Loading - Intersection Observer with loading="lazy" fallback
- Responsive Images - Automatic element with srcSet/sizes
- Smart Placeholders - Blurhash, LQIP, or standard image placeholders
- Single Component - Unified LazyImage handles all use cases
- Lightweight - ~1.4KB gzipped, minimal dependencies
- TypeScript - Complete type definitions and IntelliSense
- Accessible - Built-in ARIA support and screen reader friendly
``bash`
npm install react-lzy-img
Bundle Size: ~1.4KB gzipped • Tree-shakeable • Single dependency (blurhash)
`tsx
import { LazyImage } from 'react-lzy-img';
// Basic usage
alt="Description"
placeholder="/thumb.jpg"
width={600}
height={400}
/>
// Responsive with blurhash
srcSet="/small.jpg 400w, /large.jpg 800w"
sizes="(max-width: 600px) 100vw, 800px"
alt="Responsive image"
blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
aspectRatio={16/9}
/>
`
`tsx
// Blurhash (canvas blur effect)
// LQIP (base64 preview)
// Standard placeholder
`
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| src | string | required | Image source URL |alt
| | string | required | Image description |srcSet
| | string | - | Responsive sources (auto enables ) |sizes
| | string | - | Responsive size descriptors |placeholder
| | string | - | Placeholder image URL |blurhash
| | string | - | Blurhash string (canvas blur) |blurhashResolution
| | 16 \| 32 \| 64 | 32 | Blurhash canvas size (lower = faster) |lqip
| | string | - | Base64 LQIP |fadeIn
| | boolean | true | Fade transition |fadeInDuration
| | number | 300 | Fade duration (ms) |priority
| | boolean | false | Eager loading |preloadMargin
| | string | '200px' | Observer margin |fallback
| | ReactNode \| string | - | Error state content |className
| | string | - | CSS class for wrapper |width
| | number \| string | - | Image width |height
| | number \| string | - | Image height |aspectRatio
| | number | - | CSS aspect-ratio |style
| | CSSProperties | - | Inline styles for wrapper |loading
| | 'lazy' \| 'eager' | 'lazy' | Native loading attribute |fetchPriority
| | 'high' \| 'low' \| 'auto' | - | Browser fetch priority hint |retryAttempts
| | number | 0 | Number of retry attempts on error |retryDelay
| | number | 1000 | Base delay (ms) between retries |onLoad
| | function | - | Image load event handler |onError
| | function | - | Image error event handler |...props
| | ImgHTMLAttributes | - | Standard attributes |
`tsx
// Error handling with retry
alt="Description"
fallback="Failed to load"
retryAttempts={3}
retryDelay={1000}
onError={(e) => console.log('Error:', e)}
/>
// Priority loading (hero images) with high fetch priority
alt="Hero image"
priority
fetchPriority="high"
fadeIn={false}
/>
// Custom blurhash resolution for better performance
alt="Large image"
blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
blurhashResolution={16}
aspectRatio={16/9}
/>
// Accessibility
alt="Company logo"
role="img"
/>
`
The component uses inline styles for minimal footprint. You can customize styling via the className and style props:
`tsx`
alt="Styled image"
className="my-image"
style={{ borderRadius: '8px', overflow: 'hidden' }}
/>
React - Full support with hooks and components
`tsx`
import { LazyImage } from 'react-lzy-img';
Fully typed with IntelliSense support:
`tsx
import type { LazyImageProps } from 'react-lzy-img';
const props: LazyImageProps = {
src: '/image.jpg',
alt: 'Description',
onLoad: (event) => console.log('Loaded'), // Typed
onError: (event) => console.error('Failed'), // Typed
};
`
To use blurhash placeholders, you need to generate blurhash strings from your images. You can do this server-side or during your build process:
`bash`
npm install sharp blurhash
`js
const sharp = require('sharp');
const { encode } = require('blurhash');
async function generateBlurhash(imagePath) {
const { data, info } = await sharp(imagePath)
.raw()
.ensureAlpha()
.resize(32, 32, { fit: 'inside' })
.toBuffer({ resolveWithObject: true });
const blurhash = encode(
new Uint8ClampedArray(data),
info.width,
info.height,
4,
4
);
return blurhash;
}
// Usage
const hash = await generateBlurhash('./image.jpg');
console.log(hash); // "LEHV6nWB2yk8pyo0adR*.7kCMdnj"
`
- Blurhash.io - Upload images and get blurhash strings
- Blurhash Playground - Official playground
| Feature | Chrome | Firefox | Safari | Edge |
|---------|--------|---------|--------|------|
| IntersectionObserver | 51+ | 55+ | 12.1+ | 15+ |
| Native lazy loading | 77+ | 75+ | 15.4+ | 79+ |
| Picture element | 38+ | 38+ | 9.1+ | 13+ |
| Aspect ratio CSS | 88+ | 89+ | 15+ | 88+ |
Fallback behavior: If IntersectionObserver is not supported, images load immediately. Native lazy loading (loading="lazy") is used as a progressive enhancement.
Problem: Images remain blank or don't load
Solutions:
- Verify the src path is correct and accessiblepriority={true}
- Check browser console for network errors
- Ensure parent container has defined height/width
- Try setting to bypass lazy loading
Problem: Canvas element shows but blurhash doesn't render
Solutions:
- Verify blurhash string is valid (test at blurha.sh)
- Check browser console for decode warnings
- Ensure blurhash string is properly formatted (usually 20-30 characters)
- Try a simpler blurhash with fewer components (4x4 instead of 9x9)
Problem: Page jumps when images load
Solutions:
- Set explicit width and height propsaspectRatio
- Use prop to maintain proportionsmin-height
- Set container dimensions in parent CSS
- Use on containers
Problem: Fade-in appears janky or doesn't work
Solutions:
- Ensure fadeIn={true} is set (default)fadeInDuration
- Adjust (default 300ms)prefers-reduced-motion
- Check for CSS conflicts with opacity/transition
- User has enabled (animation disabled by design)
Problem: Page feels slow with many images
Solutions:
- Reduce preloadMargin (default "200px")priority={true}
- Use smaller placeholder images or LQIP
- Consider lower-resolution blurhash (16x16 instead of 32x32 canvas)
- Use only for above-fold images
- Compress source images
Problem: Type errors in your code
Solutions:
- Ensure @types/react is installedtsconfig.json
- Check your has "jsx": "react-jsx"import type { LazyImageProps } from 'react-lzy-img'
- Import types: rm -rf node_modules/.cache`
- Clear TypeScript cache:
---
Contributions welcome! Please read the contributing guidelines before submitting PRs.
For security vulnerabilities, please see our Security Policy.
MIT © Garrett Siegel
Feedback and contributions welcome!
- Email: garrett@garrettsiegel.com
- GitHub: github.com/garrettsiegel/react-lzy-img