Действуй как senior UX-инженер. Выбор pagination vs infinite scroll — это компромисс между discovery и retrievability. Не «современно vs устаревше». У каждого паттерна есть домены, где он провален.
Контент: {{content_type}} Возврат к элементу: {{return_pattern}} Цель: {{discoverability}}
Decision matrix
| Сценарий | Pagination | Infinite scroll | Гибрид (Load more) |
|---|---|---|---|
| Feed / timeline (Twitter, Instagram) | ✗ ломает контекст | ✓ оптимально | — |
| Search results | ✓ возврат к page-N | ✗ нет return | — |
| Catalog (e-commerce) | ✓ standard | △ требует deep links | ✓ часто лучше |
| Data table | ✓ всегда | ✗ a11y disaster | — |
| Activity log | ✓ для audit | ✓ для casual browse | ✓ |
| Comments / replies | ✗ ломает thread | △ для коротких | ✓ оптимально |
| Media gallery | △ | ✓ если discovery | ✓ |
Когда infinite scroll работает
- Discovery без цели. Юзер scroll'ит без конкретного «найти X». Examples: Instagram, Pinterest, TikTok.
- Контент = развлечение/новости. Каждый элемент самодостаточен, возврат не нужен.
- Mobile-first. Тапать «next page» на мобильном — boilerplate. Scroll естественен.
- Real-time updates (Twitter timeline). Pagination не работает с непрерывным потоком.
Когда infinite scroll НЕ работает
- Юзер должен вернуться к конкретному элементу. Search results, catalog, data table. Refresh / back → теряется place.
- Footer нужен и нужен достижимый. Infinite scroll буквально убивает footer (его не достигают).
- Accessibility critical. Screen reader юзеры не могут навигировать по бесконечно растущему списку.
- Compare между result'ами. Юзер не может «отскролл back to result #14».
- Сlear scope. «Found 247 items» — юзер хочет знать сколько всего. Infinite не показывает.
Pagination — анатомия
[< Prev] 1 2 [3] 4 5 ... Last [Next >]
- Кнопки prev/next — отдельные, не в общей нумерации.
- Текущая page — visible-different (filled).
- First / Last — особенно для больших списков (юзер хочет «к концу»).
- «...» для middle range при многих pages.
- URL persistence:
?page=2. Browser back возвращает к page-2.
Page size
- 25 / 50 / 100 — toggle в toolbar.
- Default по контексту: 25 для search, 50 для catalog, 100 для admin tables.
- Persist выбор в localStorage.
Loading state
- Skeleton-список того же размера, что и предыдущая page.
- НЕ spinner overlay (теряется контекст).
- Page-change scroll: к началу нового списка (не к началу страницы — это запутает).
Infinite scroll — анатомия
[item 1]
[item 2]
...
[item N]
─────────────
[loading indicator] ← пока подгружается
─────────────
[item N+1]
[item N+2]
Триггер подгрузки
- IntersectionObserver на «sentinel» в 200-500px от низа списка. Pre-fetch.
- НЕ scroll-event с throttle — это устарело.
Loading indicator
- Skeleton-строки в районе подгрузки (3-5 штук).
- Если ошибка — inline message «Couldn't load more. [Retry]».
End of list
- Явный markup: «You've reached the end» / «No more items» / «That's all from <date>».
- Без этого юзер scroll'ит и не понимает, кончилось ли.
Restore position
- Если юзер кликает на item → navigates away → back → должен вернуться на тот scroll position.
- В React Router / Next — restoration scroll API.
- Хранить scroll position + loaded count в session/URL state.
Гибрид: «Load more» button
Лучший из миров для catalog / activity:
[item 1]
[item 2]
...
[item 20]
[ Load more (20 of 247) ]
- Юзер контролирует загрузку.
- Footer достижим.
- Можно показать count (X of Y).
- Возврат к месту работает без сложной scroll-restoration.
Используй когда: список ограниченный (≤ 500 items), discovery + retrievability в равной мере.
URL и shareability
| Паттерн | URL behavior |
|---|---|
| Pagination | ?page=2 — share / back / refresh работает |
| Infinite scroll | ?cursor=xyz или нет URL state — share возвращает к началу |
| Load more | ?show=80 или ?cursor=xyz — work-around для share |
Если share важен — pagination или гибрид. Чистый infinite scroll — не share-friendly.
A11y
Pagination
<nav aria-label="Pagination">обёртка.- Текущая страница
aria-current="page". - Prev / next —
<a>или<button>с aria-label «Previous page».
Infinite scroll
aria-live="polite"на container'е для announce новых items.- «End of list» message с
role="status". - Skip-link к началу / поиску.
- Альтернатива «View as paginated» для assistive-tech юзеров (если возможно).
Performance
Pagination
- Server-side с cursor (offset устарел для больших dataset'ов).
- Pre-fetch следующей page on hover prev/next.
Infinite scroll
- Virtualization обязательна при >100 загруженных items (react-virtual, tanstack-virtual).
- DOM не должен расти бесконечно — иначе ноут плавится через 5 минут scroll'а.
- Trim items вне viewport (windowing).
Формат вывода
Решение
Одна строка: «<pagination / infinite / load more> — потому что <2 главных критерия>».
Spec выбранного паттерна
Если pagination:
- Page size (default + toggle options)
- Pagination UI structure
- URL scheme
- Loading state
Если infinite scroll:
- Trigger (sentinel + offset)
- Loading indicator
- End-of-list message
- Scroll restoration
- Virtualization (от какого count'а)
Если гибрид:
- Initial count
- Load more count per click
- Counter «X of Y»
Mobile-адаптация
Что меняется.
A11y чеклист
ARIA, keyboard, скрытая «paginate view» (если есть).
Альтернатива
Если выбран infinite — где запасной pagination view (например, для filtered subset).
Anti-patterns (НЕ делать)
- ❌ Infinite scroll на data table. Юзер хочет вернуться к row #234 — не может.
- ❌ Infinite scroll без end-of-list message. Юзер не знает, кончилось ли.
- ❌ Infinite scroll без virtualization при тысячах items. Memory leak, frozen UI.
- ❌ Pagination без URL. Share-link → page 1.
- ❌ Pagination, в котором current page визуально не отличается. «What page am I on?»
- ❌ Footer на странице с infinite scroll. Он недостижим.
- ❌ Infinite scroll на admin / data tables, где юзер сортирует и сравнивает. Невозможно.
- ❌ «Load more» без счёта. Юзер не знает, сколько всего.
- ❌ Infinite scroll без restore scroll position. Click → back → начало списка.
- ❌ Infinite scroll, который добавляет новые items сверху во время чтения. Юзер теряет место.
Полный UX-аудит сайта
Эвристическая оценка по Нильсену + проверка ключевых сценариев. На выходе — приоритизированный список проблем.
Аудит производительности (Core Web Vitals)
Глубокая проверка LCP, INP, CLS с привязкой к коду и приоритизированным планом исправлений.
Аудит доступности по WCAG 2.2 AA
Проверка контраста, клавиатурной навигации, скринридеров, фокус-индикаторов и ARIA.