Infinite scroll
Also called endless scrolling, infinite scrolling, or infinite feed
A list that loads the next batch of items automatically as you scroll near the bottom, so the feed seems to never end.
import { useEffect, useRef, useState } from 'react';
const PAGE_SIZE = 20;
// Start fetching while the sentinel is still 600px below the viewport.
const PRELOAD_MARGIN = '0px 0px 600px 0px';
type Activity = { id: string; text: string };
type Status = 'idle' | 'loading' | 'error' | 'done';
type Props = { fetchPage: (page: number, size: number) => Promise<Activity[]> };
export function ActivityFeed({ fetchPage }: Props) {
const [items, setItems] = useState<Activity[]>([]);
const [page, setPage] = useState(0);
const [status, setStatus] = useState<Status>('idle');
const sentinel = useRef<HTMLDivElement>(null);
useEffect(() => {
const element = sentinel.current;
if (!element || status !== 'idle') return;
const observer = new IntersectionObserver(
([entry]) => {
if (!entry?.isIntersecting) return;
setStatus('loading');
fetchPage(page, PAGE_SIZE).then(
(next) => {
setItems((current) => [...current, ...next]);
setPage(page + 1);
setStatus(next.length < PAGE_SIZE ? 'done' : 'idle');
},
() => setStatus('error')
);
},
{ rootMargin: PRELOAD_MARGIN }
);
observer.observe(element);
return () => observer.disconnect();
}, [fetchPage, page, status]);
return (
<div className='w-96 text-sm'>
<ul className='divide-y *:py-3'>
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
{/* The invisible marker after the last item. */}
<div ref={sentinel} />
<p role='status' className='py-4 text-center text-muted-foreground'>
{status === 'loading' && 'Loading more…'}
{status === 'done' && 'You’re all caught up'}
{status === 'error' && (
<button onClick={() => setStatus('idle')} className='text-red-600'>
Couldn’t load more. Try again
</button>
)}
</p>
</div>
);
}Make the activity feed load 20 more items when the reader scrolls within 600px of the bottom: put a sentinel div after the last item, watch it with an IntersectionObserver using rootMargin "0px 0px 600px 0px", and show a small spinner row while fetching. Stop with a "You're all caught up" line when there are no more items, and restore the scroll position when the reader navigates back.
Paste into Layerwise or any AI coding tool.
What is Infinite scroll?
An infinitely scrolling list starts with one batch of items. As the reader gets close to the end, the app fetches the next batch, shows a small spinner while it loads, and appends the new items below. There are no page numbers to click.
It suits feeds people browse without a specific goal: social timelines, photo galleries, news, and product discovery. It is a poor fit when people hunt for one item, compare results, or need the footer, because the footer keeps moving away and there is no “page 4” to return to. Pagination serves those cases better.
Start loading well before the reader hits the bottom, about a screen ahead, by watching an invisible marker below the last item with an IntersectionObserver. Restore the scroll position when someone comes back to the list, show a clear end message, and offer a “Load more” button as a fallback. For very long feeds, virtualize the list so only the visible rows stay in the page.
Often confused with
- Pagination
- Pagination splits items into numbered pages that people move between on purpose. Infinite scroll appends more items to one list automatically.
- Lazy loading
- Lazy loading delays images or code until they are about to be seen. Infinite scroll uses the same kind of trigger to fetch and add whole new items to a list.
Related terms
All termsPagination
Navigation that splits a long list into numbered pages with Previous and Next buttons, so people browse a fixed number of items at a time.
Lazy loading
A technique that waits to load images, videos, or code until they are about to scroll into view, so the first screen loads faster.
Spinner
A small looping animation, usually a rotating ring, that shows the app is busy with a short task of unknown length.
Skeleton screen
A loading placeholder made of gray blocks shaped like the content on its way, often with a soft shimmer, that the real content replaces.
Build it with Layerwise
Describe your app, watch it run in a live preview, and publish it with a database, storage, and sign-in built in.