gentleduck lazy
It is a lightweight and accessible React library for lazy-loading images and components. It uses the IntersectionObserver API to trigger loading when content enters the viewport, ensuring smooth performance and accessibility.
Features
- Lazy loading for components and images
- Customizable with IntersectionObserver options
- Accessibility-first: ARIA roles, live regions, focus management
- Placeholder support while content loads
- Composable hooks for custom behavior
Installation
npm install @gentleduck/lazy
npm install @gentleduck/lazy
Usage
1) Lazy Component
The DuckLazyComponent defers rendering until its children enter the viewport.
import { DuckLazyComponent } from '@gentleduck/lazy'
function MyComponent() {
return (
<DuckLazyComponent options={{ rootMargin: '100px', threshold: 0.25 }}>
<div>Content that will be lazily loaded</div>
</DuckLazyComponent>
)
}import { DuckLazyComponent } from '@gentleduck/lazy'
function MyComponent() {
return (
<DuckLazyComponent options={{ rootMargin: '100px', threshold: 0.25 }}>
<div>Content that will be lazily loaded</div>
</DuckLazyComponent>
)
}Props
options?: IntersectionObserver options (rootMargin,threshold)children: The lazy-loaded content
2) Lazy Image
The DuckLazyImage supports placeholders, accessibility attributes, and Next.js next/image.
import { DuckLazyImage } from '@gentleduck/lazy'
function MyImageComponent() {
return (
<DuckLazyImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="A description of the image"
width={400}
height={300}
options={{ rootMargin: '100px', threshold: 0.25 }}
/>
)
}import { DuckLazyImage } from '@gentleduck/lazy'
function MyImageComponent() {
return (
<DuckLazyImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="A description of the image"
width={400}
height={300}
options={{ rootMargin: '100px', threshold: 0.25 }}
/>
)
}Props
src: URL of the image (required)placeholder?: Placeholder URL while loadingalt: Accessible description (required)width/height: Image size (required)options?: IntersectionObserver optionsnextImage?: Enables Next.jsnext/imageoptimization
3) useLazyLoad Hook
Attach lazy-loading behavior to any element.
import { useLazyLoad } from '@gentleduck/lazy'
function MyComponent() {
const { isVisible, elementRef } = useLazyLoad({
rootMargin: '100px',
threshold: 0.25,
})
return (
<div ref={elementRef}>
{isVisible ? <div>Visible content</div> : <div>Loading...</div>}
</div>
)
}import { useLazyLoad } from '@gentleduck/lazy'
function MyComponent() {
const { isVisible, elementRef } = useLazyLoad({
rootMargin: '100px',
threshold: 0.25,
})
return (
<div ref={elementRef}>
{isVisible ? <div>Visible content</div> : <div>Loading...</div>}
</div>
)
}Returns
isVisible:boolean→ if element is visibleelementRef:React.Ref→ attach to observed element
4) useLazyImage Hook
Specialized hook for images: manages visibility + load state.
import { useLazyImage } from '@gentleduck/lazy'
function LazyImage({ src, placeholder }) {
const { isLoaded, imageRef } = useLazyImage(src, {
rootMargin: '100px',
threshold: 0.25,
})
return (
<div ref={imageRef}>
{!isLoaded && <img src={placeholder} alt="Placeholder" />}
{isLoaded && <img src={src} alt="Main Image" />}
</div>
)
}import { useLazyImage } from '@gentleduck/lazy'
function LazyImage({ src, placeholder }) {
const { isLoaded, imageRef } = useLazyImage(src, {
rootMargin: '100px',
threshold: 0.25,
})
return (
<div ref={imageRef}>
{!isLoaded && <img src={placeholder} alt="Placeholder" />}
{isLoaded && <img src={src} alt="Main Image" />}
</div>
)
}Returns
isLoaded:boolean→ if image finished loadingimageRef:React.Ref→ attach to<img>
DuckLazyImage Component (Detailed)
Optimized lazy image loader with placeholder + accessibility.
import { DuckLazyImage } from '@gentleduck/lazy'
function MyImageComponent() {
return (
<DuckLazyImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="Mountain view"
width={400}
height={300}
options={{ rootMargin: '100px', threshold: 0.25 }}
/>
)
}import { DuckLazyImage } from '@gentleduck/lazy'
function MyImageComponent() {
return (
<DuckLazyImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="Mountain view"
width={400}
height={300}
options={{ rootMargin: '100px', threshold: 0.25 }}
/>
)
}Integration with Next.js
Enable Next.js image optimization with nextImage.
<DuckLazyImage
nextImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="Next.js optimized image"
width={400}
height={300}
/><DuckLazyImage
nextImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="Next.js optimized image"
width={400}
height={300}
/>Benefits:
- Built-in Next.js optimization
- Seamless lazy loading
Integration with React
Works as a drop-in replacement for <img> in plain React apps.
<DuckLazyImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="React lazy image"
width={400}
height={300}
/><DuckLazyImage
src="https://example.com/image.jpg"
placeholder="https://example.com/placeholder.jpg"
alt="React lazy image"
width={400}
height={300}
/>Accessibility Features
aria-live="polite"→ announces loading statearia-hidden→ hides placeholders from ATrole="img"+aria-labelfor screen readersaria-atomic+aria-relevantfor granular updates