Paginação vs scroll infinito
Uma página web com 500 registros não consegue renderizar todos de uma vez. Há duas soluções: a paginação, que divide os registros em páginas separadas entre as quais o usuário navega, e o scroll infinito, que carrega mais registros conforme o usuário rola até o fim. Cada padrão tem prós e contras, e uma aplicação Rails bem feita precisa dos dois.
Paginação tradicional: Pagy
Esta aplicação usa a gem Pagy para paginação por offset na área admin. A lista de usuários do admin mostra 10 usuários por página, com links numerados:
# Gemfile
gem "pagy", "~> 43.3"
# config/initializers/pagy.rb
Pagy::OPTIONS[:limit] = 10
O query object limita o número da página às páginas que existem e depois calcula o offset:
# app/queries/admin/identity/list_users_query.rb
def self.call(search: nil, role: nil, page: 1, per_page: Pagy::OPTIONS[:limit] || Pagy::DEFAULT[:limit])
users = ::Identity::User.order(created_at: :desc)
# ... filtros de search/role restringem `users` aqui ...
total_count = users.count
page = Admin::PageNumber.clamp(page, total_count: total_count, per_page: per_page)
offset = (page - 1) * per_page
paginated_users = users.limit(per_page).offset(offset)
Result.new(users: paginated_users, total_count: total_count)
end
A página 1 mostra os usuários 1 a 10, e a página 2, os usuários 11 a 20. O usuário clica em "Próxima" ou num número de página para navegar, e cada clique é uma requisição completa que troca o conteúdo da página.
O helper de paginação renderiza os controles:
<%# app/views/admin/identity/users/index.html.erb %>
<% pagy = admin_paginator(total_count: @total_count, per_page: @per_page) %>
<div class="mt-8 flex justify-center">
<%= admin_pagination(pagy) if pagy.pages > 1 %>
</div>
É o padrão clássico. Ele funciona bem em interfaces de admin, em que o usuário precisa pular para uma página específica, guardar uma posição ou compartilhar um link para "a página 3 de usuários". A URL guarda a posição: /admin/users?page=3.
Quando a paginação não serve
A paginação funciona em painéis de admin e resultados de busca. Ela falha em feeds de conteúdo (timelines sociais, threads de comentários, logs de atividade), em que o usuário pensa num fluxo contínuo, e não numa grade numerada.
Os problemas:
- Perda de contexto: clicar em "Página 2" troca a página inteira, e o usuário perde a posição de rolagem e o que estava lendo.
- Números de página sem sentido: a página 7 de uma thread de comentários não diz nada sobre o conteúdo. Páginas de um feed não têm identidade estável, já que conteúdo novo desloca tudo.
- Interrupção: o usuário precisa parar de ler, achar o controle de paginação, clicar, esperar a página e depois achar onde continuar. Cada fronteira de página acrescenta atrito.
Scroll infinito: o fluxo contínuo
O scroll infinito elimina as fronteiras de página. Quando o usuário rola perto do fim, a aplicação busca o próximo lote de registros e o acrescenta à lista, e o usuário continua rolando sem pausa.
sequenceDiagram
participant U as Usuário
participant B as Navegador
participant S as Servidor
U->>B: Rola para baixo
B->>B: IntersectionObserver dispara
B->>S: GET /comments?page=2 (Turbo Stream)
S->>B: turbo_stream.append + replace load-more
B->>B: Novos comentários aparecem abaixo dos existentes
U->>U: Continua rolando sem interrupção
A thread de comentários desta aplicação já usa uma versão manual desse padrão: um botão "Carregar comentários anteriores" que acrescenta comentários via Turbo Streams. O scroll infinito automatiza o clique. Em vez de o usuário clicar nele, o navegador percebe que ele está perto do fim e dispara a carga sozinho.
Quando usar cada um
| Padrão | Melhor para | Exemplos nesta aplicação |
|---|---|---|
| Paginação | Listas de admin, resultados de busca, tabelas | Lista de usuários do admin, lista de cursos do admin |
| Botão carregar mais | Threads de comentários, respostas | Discussão nas aulas |
| Scroll infinito | Feeds de atividade, timelines, conteúdo contínuo | Ainda não implementado; este curso o constrói |
O fator decisivo é o que o usuário quer fazer. No admin, ele quer pular para uma página específica, filtrar e compartilhar URLs. Num feed, quer ler o conteúdo em ordem, sem interrupção.
O que este curso constrói
Nas próximas seis aulas, você vai montar um scroll infinito com ferramentas que esta aplicação já tem:
- IntersectionObserver (aula 2): a API do navegador que detecta quando um elemento entra na viewport, já usada no navbar desta aplicação para ligar uma sombra durante a rolagem.
- Turbo Frames (aula 3): como esta aplicação usa frames para conteúdo paginado na área admin.
- Turbo Streams append (aula 4): como a thread de comentários acrescenta registros novos sem trocar a página, a base do scroll infinito.
- O controller Stimulus (aula 5): um controller
infinite-scrollreutilizável que combina IntersectionObserver com Turbo Streams. - Casos-limite (aula 6): estados de carregamento, tratamento de erros, restauração da rolagem e SEO.
- Juntando tudo (aula 7): o padrão completo, da consulta no servidor ao conteúdo renderizado.
Continue com a aula 2
IntersectionObserver: o detector de scroll
Explica como o IntersectionObserver e um elemento sentinela detectam quando o usuário chega perto do fim da lista.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.