typescriptintermediate
Infinite Scroll with Intersection Observer
Load more items as the user scrolls using IntersectionObserver. No external libraries required.
typescriptPress ⌘/Ctrl + Shift + C to copy
import { useEffect, useRef, useState, useCallback } from 'react';
interface UseInfiniteScrollOpts<T> {
fetchPage: (page: number) => Promise<T[]>;
initialPage?: number;
}
export function useInfiniteScroll<T>({ fetchPage, initialPage = 1 }: UseInfiniteScrollOpts<T>) {
const [items, setItems] = useState<T[]>([]);
const [page, setPage] = useState(initialPage);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const sentinelRef = useRef<HTMLDivElement | null>(null);
const loadMore = useCallback(async () => {
if (loading || !hasMore) return;
setLoading(true);
const newItems = await fetchPage(page);
if (newItems.length === 0) {
setHasMore(false);
} else {
setItems((prev) => [...prev, ...newItems]);
setPage((p) => p + 1);
}
setLoading(false);
}, [fetchPage, page, loading, hasMore]);
useEffect(() => {
const sentinel = sentinelRef.current;
if (!sentinel) return;
const observer = new IntersectionObserver(
([entry]) => { if (entry.isIntersecting) loadMore(); },
{ rootMargin: '200px' }
);
observer.observe(sentinel);
return () => observer.disconnect();
}, [loadMore]);
return { items, loading, hasMore, sentinelRef };
}
// Usage:
// const { items, loading, sentinelRef } = useInfiniteScroll({
// fetchPage: (page) => fetch(`/api/posts?page=${page}`).then(r => r.json()),
// });
// return <>{items.map(renderItem)}<div ref={sentinelRef} /></>;Use Cases
- Feed pagination
- Product listing pages
- Comment threads
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptintermediate
React Intersection Observer Hook
Custom useIntersectionObserver hook for lazy loading, infinite scroll, and scroll animations.
Best for: Lazy loading components when they enter viewport
#react#hooks
typescriptbeginner
useDebounce — Debounced Value Hook
Debounce any rapidly-changing value with a configurable delay. Useful for search inputs and resize handlers.
Best for: Search-as-you-type
#hooks#debounce
typescriptintermediate
useIntersectionObserver Hook
Track element visibility with the Intersection Observer API for lazy loading and scroll animations.
Best for: Lazy loading images
#hooks#intersection-observer
typescriptintermediate
useThrottle Hook for Rate Limiting
Throttle rapidly-firing values like scroll or resize events with a configurable delay hook.
Best for: Scroll position tracking
#react#hooks