JAVASCRIPT
Implement Lazy Loading and Animations with useIntersectionObserver Hook
Leverage the useIntersectionObserver hook in React to detect when an element enters or exits the viewport, enabling features like lazy loading images or scroll-triggered animations.
import React, { useRef, useState, useEffect } from 'react';
function useIntersectionObserver(options) {
const [entry, setEntry] = useState(null);
const targetRef = useRef(null);
useEffect(() => {
const node = targetRef.current;
if (!node) return;
const observer = new IntersectionObserver(([singleEntry]) => {
setEntry(singleEntry);
}, options);
observer.observe(node);
return () => {
observer.disconnect();
};
}, [options]); // Re-run if options change
return [targetRef, entry];
}
function LazyImage({ src, alt }) {
const [imgRef, entry] = useIntersectionObserver({
threshold: 0.1, // Trigger when 10% of the target is visible
rootMargin: '0px 0px -100px 0px', // Start loading 100px before reaching bottom of viewport
});
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
if (entry?.isIntersecting) {
setIsVisible(true);
}
}, [entry]);
return (
<div ref={imgRef} style={{ height: '300px', background: '#eee', marginBottom: '20px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{isVisible ? (
<img src={src} alt={alt} style={{ maxWidth: '100%', maxHeight: '100%' }} />
) : (
<p>Scroll down to load image...</p>
)}
</div>
);
}
function IntersectionObserverDemo() {
return (
<div style={{ height: '200vh', padding: '20px' }}>
<h1>Scroll Down to See Lazy Loading</h1>
<div style={{ height: '800px' }}>
<p>Some content above the image...</p>
</div>
<LazyImage
src="https://picsum.photos/600/300"
alt="A random beautiful landscape"
/>
<div style={{ height: '800px' }}>
<p>Some content below the image...</p>
</div>
</div>
);
}
export default IntersectionObserverDemo;
How it works: This custom `useIntersectionObserver` hook provides a reactive way to detect when an element enters or exits the browser's viewport. It takes `IntersectionObserver` options and returns a ref to attach to the target element, along with the current `IntersectionObserverEntry`. In the `LazyImage` example, we use it to conditionally render an image only when it becomes visible in the viewport (or slightly before, thanks to `rootMargin`), effectively implementing lazy loading. This conserves bandwidth and improves initial page load performance.