Docs
gentleduck lazy
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/image
optimization
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-label
for screen readersaria-atomic
+aria-relevant
for granular updates