Перейти к содержанию
Innovialabs

Бесконечная прокрутка, пагинация или «Показать ещё»: что выбрать

Практика3 мин

Коротко

Для каталогов и поиска, где важно найти и вернуться, выбирайте пагинацию или «Показать ещё» с адресами страниц; бесконечная прокрутка подходит лентам, которые просматривают без цели.

Короткий ответ

  • Пагинация — когда пользователь ищет конкретное: каталог, результаты поиска, архив. Понятно, где он находится, легко вернуться и поделиться ссылкой.
  • «Показать ещё» — компромисс для каталогов и блогов: подгрузка без перехода, но пользователь сам решает, когда грузить, и до футера можно дойти.
  • Бесконечная прокрутка — для лент, которые листают без конкретной цели: соцсети, подборки, видео.

Если сомневаетесь, начните с «Показать ещё» + URL вида ?page=N: этот вариант реже всего ломает навигацию и SEO.

Сравнение

Пагинация«Показать ещё»Бесконечная прокрутка
UXконтроль и ориентирконтроль без перезагрузкиминимум усилий, легко потеряться
Футердоступендоступенчасто недостижим
Производительностьлёгкие страницыDOM растёт по запросуDOM растёт постоянно
SEOлучший вариантхорошо, если есть ссылки на страницыплохо без отдельных URL
Аналитикапросмотры страниц из коробкинужны событиянужны события и глубина прокрутки
Возврат «назад»работает самнужно сохранять состояниенужно сохранять состояние

SEO: что учесть

Поисковые роботы не прокручивают страницу и не нажимают кнопки. Чтобы весь список был проиндексирован:

  • у каждой порции должен быть свой URL (/catalog?page=2);
  • на странице должны быть обычные ссылки <a href> на следующую страницу, даже если JavaScript перехватывает клик;
  • не закрывайте страницы пагинации от индексации и не ставьте на них canonical на первую страницу — каждая страница должна быть каноничной сама для себя.

Курсорная пагинация в API

Пагинация через OFFSET проста, но на больших таблицах становится медленнее, а при добавлении новых записей элементы дублируются или пропускаются. Курсор решает обе проблемы: клиент передаёт идентификатор последнего полученного элемента.

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

API возвращает список и nextCursor — значения последнего элемента, закодированные в строку. Если nextCursor пустой, данных больше нет. Для сортировки нужен индекс по (created_at, id).

Курсор не позволяет прыгнуть сразу на страницу 37, поэтому для классической пагинации с номерами страниц чаще оставляют OFFSET, а курсор используют для ленты и «Показать ещё».

Подгрузка через Intersection Observer

Не слушайте событие scroll — используйте Intersection Observer и невидимый элемент-«датчик» в конце списка.

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 начинает загрузку заранее, чтобы пользователь не упирался в пустоту. Для «Показать ещё» тот же код вызывается по клику на кнопку.

Сохранение позиции при возврате назад

Самая раздражающая проблема: пользователь открыл товар, нажал «Назад» — и снова видит первую страницу. Что помогает:

  • Обновляйте URL через history.replaceState по мере подгрузки (?page=3), чтобы после возврата знать, сколько грузить.
  • Кэшируйте загруженные данные в sessionStorage или в кэше запросов (TanStack Query, SWR) и восстанавливайте список до того, как браузер попытается вернуть прокрутку.
  • Сохраняйте позицию прокрутки перед уходом и возвращайте её после отрисовки списка.
  • В SPA-фреймворках проверьте, как работает их встроенное восстановление прокрутки — часто оно справляется само, если данные уже в кэше.

Производительность длинных списков

  • После нескольких сотен карточек включайте виртуализацию — в DOM остаются только видимые элементы.
  • Задавайте размеры изображениям и используйте loading="lazy".
  • Показывайте скелетоны на время загрузки, чтобы не прыгал макет.

Частые вопросы

Если элементы доступны только после прокрутки и у них нет своих URL, робот увидит лишь первую порцию. Добавьте страницы с отдельными адресами и обычные ссылки на них, тогда индексация сохранится.

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.