Cursor vs offset pagination: quando usar cada
Descubra, de forma prática, as diferenças e limitações entre cursor-based pagination e offset-based pagination e saiba quando escolher cada abordagem.
Por que isso é importante
Cursor based pagination vs offset: quando usar cada uma? Offset para jump-to-page (admin). Cursor/keyset para infinite scroll e feeds grandes — evita deep OFFSET e shifting window. Cursor opaco + LIMIT+1 para hasMore.
Leitura relacionada: paginação cursor na prática · como criar API REST com Node.js · guia de normalização de banco · SQL para quem veio do NoSQL · tipos de filas no RabbitMQ.
O que é Offset-Based Pagination?
Offset-based pagination usa LIMIT + OFFSET: página N = pular (N−1)×pageSize registros. Simples e permite jump-to-page — mas em tabelas grandes o deep OFFSET fica lento e inserts/deletes deslocam a janela.
Atenção
O offset no banco de dados exige leitura sequencial dos registros, o que pode causar lentidão em grandes volumes de dados.
O que é Cursor-Based Pagination?
Cursor-based (keyset) pagination usa um marcador do último item (ex.: created_at+id). A próxima página pede “depois deste cursor” — estável em feeds e infinite scroll, sem pular OFFSET profundo.
Atenção
No cursor-based, não existe o conceito nativo de página específica. O usuário só consegue avançar ou retroceder sequencialmente.
Quando o Offset não representa a ordem de exibição?
OFFSET na ordem do ID interno só funciona se a UI ordenar pelo mesmo critério. Se a lista ordena por posição, published_at ou score, o “pulo” de registros não bate com a página que o usuário vê.
Atenção
Assumir que o ID sempre é a ordem exata pode comprometer a lógica de paginação — principalmente quando registros podem ser reordenados, editados ou removidos.
Como funciona a navegação para páginas específicas no Offset?
Na estratégia offset, a possibilidade de navegar diretamente para qualquer página é uma de suas maiores vantagens. Exemplo: se há 20 posts por página e você quer acessar a página 21, basta pular os 400 primeiros (20 x 20) e retornar do item 401 ao 420.
Limitações da Cursor-Based Pagination
O cursor-based pagination não permite acesso direto a uma página arbitrária — sempre é necessário passar pelo(s) cursor(es) anterior(es). Isso pode dificultar para quem precisa pular para uma página específica, mas aumenta a eficiência para buscas sequenciais e scroll infinito.
Quando usar Cursor-Based Pagination?
Use cursor/keyset quando o feed é grande ou infinite scroll: próxima página = “depois deste cursor”, sem deep OFFSET nem janela que desloca com inserts.
Atenção
Cursor-based não permite “pular” para a página 20 instantaneamente; exige navegação por etapas.
Quando usar Offset-Based Pagination?
Use offset quando a UI precisa de jump-to-page N (tabela admin, páginas numeradas) e o dataset cabe sem deep OFFSET caro.
Comparativo prático: offset vs cursor (e keyset)
Resumo: offset = navegar por número de página; cursor/keyset = estabilidade e escala em feeds. Keyset é o cursor “de verdade” no SQL; cursor opaco só empacota o keyset.
Offset-Based Pagination
Paginação tradicional por saltos (offsets)
Prós
- Permite navegar direto para qualquer página
- Controle total de páginas e ordenações variadas
Contras
- Escala mal em grandes tabelas
- Pode exibir resultados inconsistentes se dados mudarem durante a navegação
Cursor-Based Pagination
Paginação por marcadores (cursored navigation)
Prós
- Alta performance em tabelas extensas
- Ideal para scroll infinito e navegação sequencial
Contras
- Não permite saltar para uma página arbitrária
- A navegação é feita por etapas sequenciais apenas
Implementação Node + Postgres: cursor opaco
Padrão colável (Node + Postgres): cursor opaco = base64url de { createdAt, id }. Índice composto (created_at, id). Query keyset com tuple comparison e LIMIT+1 para hasMore:
WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT $3 — decode do cursor alimenta $1/$2; se vierem N+1 rows, pop a extra e encode nextCursor. Envelope: { items, nextCursor, hasMore }. Mesmo modelo mental do Convex .paginate (cursor + página) — sem inventar benchmarks de latency.
Tie-breaker
Nunca paginar só por created_at se houver empates — o id desempata e evita duplicar/pular linhas.
Ferramentas: Relay, libs e Convex.paginate
TypeORM
ORM TypeScript compatível com estratégias offset e cursor
Apollo GraphQL
Suporte pronto a paginação cursor-based através de connections
Sequelize
ORM popular para Node.js com suporte tradicional a offset
Principais erros ao escolher paginação
Dica
Sempre alinhe o tipo de paginação com as expectativas de uso definidas junto ao time de produto.
Árvore de decisão: qual paginação escolher
Analise cuidadosamente o volume de dados envolvido, os requisitos de navegação do seu sistema e o padrão de acesso mais utilizado pelos usuários antes de escolher entre offset ou cursor. A escolha certa pode representar a diferença entre uma experiência fluida ou frustrante.
Admin jump-to-page vs infinite scroll
Tabela de decisão rápida:
Offset (admin)
Painel com “ir à página N” e ordenação arbitrária.
Prós
- Jump-to-page familiar
- Ordenação livre no grid
Contras
- Deep OFFSET fica caro em tabelas grandes
- Aceite o custo ou filtre por data/id antes
Cursor / keyset (feed)
Timeline, infinite scroll e sync mobile.
Prós
- Feed estável sob inserts
- Bom para mobile e sync
Contras
- Não finge número de página estável
- Precisa índice composto (created_at, id)
API híbrida
Offset só em /admin; cursor em /feed.
Prós
- Contrato certo por superfície
- Documenta os dois modos
Contras
- Não misture os contratos no mesmo endpoint sem documentar
Se o admin precisar de jump e o volume explodir, filtre por data/id antes do OFFSET ou ofereça busca. Inclua índice composto (created_at, id) e truque LIMIT+1 para hasNext.
Checklist de Implementação
Checklist de Implementação
Fontes
Revisão em agosto de 2026. Offset vs cursor/keyset é trade-off de produto (jump-to-page vs feed estável) — valide no seu banco e ORM. Exemplos didáticos, não benchmark universal de latência.
<a href="https://docs.convex.dev/database/pagination">Convex — Pagination</a>. <a href="https://www.postgresql.org/docs/current/queries-limit.html">PostgreSQL — LIMIT/OFFSET</a>. <a href="https://use-the-index-luke.com/no-offset">Use The Index, Luke — No OFFSET</a>.
Perguntas frequentes
Qual a diferença entre cursor-based pagination e offset?
Offset usa LIMIT/OFFSET (página N); cursor/keyset busca a partir de um ponto estável (id + created_at). Cursor escala melhor em feeds grandes; offset é simples e ok em listas pequenas.
Quando usar offset pagination?
Use offset em admin, tabelas pequenas ou quando o usuário precisa pular para a página N. Evite em feeds com inserts/deletes frequentes ou páginas muito profundas.
Cursor pagination permite pular para a página N?
Em geral não de forma estável: o cursor marca posição na ordenação, não um número de página. Para infinite scroll e APIs grandes, isso é o trade-off correto.
Como fazer cursor pagination no PostgreSQL?
Ordene com tie-breaker (ex.: created_at, id), filtre WHERE (created_at, id) < última tupla, LIMIT+1 para has_next e encode um cursor opaco. No Convex, o mental model é o mesmo com .paginate.