In short
Instagram and YouTube never show a "Next page" button. They load more as you scroll. Building that yourself teaches you pagination, observers and how not to flood a server.
A feed that loads more content automatically as the user scrolls near the bottom — practice for the Intersection Observer API and paginated data fetching. Infinite scrolling also requires careful state management to avoid duplicate requests, race conditions, repeated loading, inaccessible content, and poor scroll performance.

Think of it like this
The thali refill
At a good thali place, the server tops up your sambar the moment your bowl looks low. You never have to ask. Infinite scroll watches the bottom of the page the same way and fetches more just before you run out.
Watch it happen
Loading the next page
sentinel near (200px)
1/7The sentinel comes within 200px of the screen, so the callback fires.
The big ideas
What you need to know
Watch a sentinel
Put an empty div after the list. IntersectionObserver tells you when it scrolls into view, no scroll event needed.
Track the page
Keep a page number and ask the API for the next page each time, like ?_page=2&_limit=10.
Guard against double loads
A loading flag stops a second request starting while the first one is still running.
Know when to stop
When the API returns an empty page, stop observing and show "You're all caught up".
How it works
Build plan
- 1
First page
Fetch and render the first 10 items on page load.
- 2
Add a sentinel
An empty div after the list that you'll watch.
- 3
Observe it
IntersectionObserver calls loadMore when the sentinel is near the screen.
- 4
Append, don't replace
Add new items to the end and bump the page number.
- 5
Stop and polish
End message, loading spinner and an error retry button.
See it in code
Walk through the code
1let page = 1;2let loading = false;3let done = false;4const sentinel = document.querySelector("#sentinel");5 6const observer = new IntersectionObserver(async ([entry]) => {7 if (!entry.isIntersecting || loading || done) return;8 loading = true;9 const res = await fetch(`https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=10`);10 const posts = await res.json();11 if (posts.length === 0) { done = true; observer.disconnect(); }12 else { renderPosts(posts); page++; }13 loading = false;14}, { rootMargin: "200px" });15 16observer.observe(sentinel);1/4Track the page, a loading guard, an end flag and the sentinel.
rootMargin: "200px" starts loading when the sentinel is 200px away from the screen, so new posts are ready before the user hits the bottom.
Save yourself the pain
Common mistakes
Oops
Listening to every scroll event and checking positions by hand.
Do this instead
It fires constantly and is easy to get wrong. IntersectionObserver does the watching for you.
Oops
No loading flag, so fast scrolling fires the same page twice.
Do this instead
Set loading = true before fetching and back to false after, and skip if it's already true.
Oops
Putting important links in a footer below an infinite list.
Do this instead
Users can never reach it. Move them elsewhere or switch to a "Load more" button.
Your mission
Milestone 1: load page two
Quick check
What does rootMargin: "200px" do in the observer above?
Go deeper
- 1Use the Intersection Observer API to detect when the user nears the bottom. Verify the behavior with realistic data and handle the failure or empty state as well.
- 2Show a loading indicator while the next page of data fetches. Validate your understanding by applying it in a small practical example.
- 3Handle the 'end of results' state so it doesn't loop or error out.
More projects at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.