Skip to content
Innovialabs

Infinite Scroll vs Pagination vs Load More: Which to Build

Practice4 min

In short

For catalogs and search, where people need to find and return, use pagination or Load More with page URLs; infinite scroll suits feeds that people browse without a specific goal.

The short answer

  • Pagination — when people look for something specific: a catalog, search results, an archive. They know where they are, can go back easily and share a link.
  • Load More — a middle ground for catalogs and blogs: loading without navigation, but the user decides when to load, and the footer stays reachable.
  • Infinite scroll — for feeds people browse without a specific goal: social feeds, collections, video.

If unsure, start with Load More plus a ?page=N URL: it is the option least likely to break navigation and SEO.

Comparison

PaginationLoad MoreInfinite scroll
UXcontrol and orientationcontrol without reloadsleast effort, easy to get lost
Footerreachablereachableoften unreachable
Performancelight pagesDOM grows on demandDOM grows constantly
SEObest optiongood if page links existpoor without separate URLs
Analyticspage views out of the boxneeds eventsneeds events and scroll depth
Back navigationworks by itselfstate must be savedstate must be saved

SEO considerations

Search crawlers do not scroll and do not click buttons. To get the whole list indexed:

  • each chunk needs its own URL (/catalog?page=2);
  • the page must contain regular <a href> links to the next page, even if JavaScript intercepts the click;
  • do not block paginated pages from indexing and do not point their canonical to page one — each page should be canonical to itself.

Cursor-based API pagination

OFFSET pagination is simple, but it gets slower on large tables, and when new rows are added, items get duplicated or skipped. A cursor solves both: the client sends the position of the last item it received.

SELECT id, title, created_at
FROM posts
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;

The API returns the list and a nextCursor — the last item's values encoded into a string. An empty nextCursor means there is no more data. Sorting needs an index on (created_at, id).

A cursor cannot jump straight to page 37, so classic numbered pagination usually keeps OFFSET, while cursors serve feeds and Load More.

Loading with Intersection Observer

Do not listen to the scroll event — use an Intersection Observer and an invisible "sentinel" element at the end of the list.

const sentinel = document.querySelector("#sentinel");
let cursor = null;
let loading = false;

const io = new IntersectionObserver(async ([entry]) => {
  if (!entry.isIntersecting || loading) return;
  loading = true;
  const res = await fetch(`/api/posts?cursor=${encodeURIComponent(cursor ?? "")}`);
  const { items, nextCursor } = await res.json();
  renderItems(items);
  cursor = nextCursor;
  loading = false;
  if (!nextCursor) io.disconnect();
}, { rootMargin: "400px" });

io.observe(sentinel);

rootMargin starts loading early so the user never hits an empty edge. For Load More, run the same code on a button click.

Preserving scroll position on back navigation

The most annoying issue: a user opens a product, presses Back and sees page one again. What helps:

  • Update the URL with history.replaceState as chunks load (?page=3), so after returning you know how much to load.
  • Cache loaded data in sessionStorage or a request cache (TanStack Query, SWR) and restore the list before the browser tries to restore scroll.
  • Save the scroll position before leaving and restore it after the list renders.
  • In SPA frameworks, check how built-in scroll restoration behaves — it often works on its own when data is already cached.

Performance of long lists

  • After a few hundred cards, turn on virtualization so only visible items stay in the DOM.
  • Set image dimensions and use loading="lazy".
  • Show skeletons while loading so the layout does not jump.

FAQ

If items only appear after scrolling and have no URLs of their own, the crawler sees only the first chunk. Add pages with separate addresses and plain links to them, and indexing will be preserved.

We can help with this

If you'd rather get the result than figure it all out yourself — this is what we do every day.

Discuss a project

Read next

Have an idea or a task? Tell us about it

We'll reply within one business day and propose a solution. The consultation is free.