Pular para o conteúdo
Backend

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.

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.

  1. Passo 1: Avalie se o volume de dados é alto e o scroll infinito
    é desejado para UX moderna.
  2. Passo 2: Implemente quando performance na navegação sequencial
    for crítica e não há necessidade de pular direto para uma página distante.

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.

  1. Passo 1: Escolha quando o usuário precisa de navegação direta
    por páginas (como em tabelas administrativas).
  2. Passo 2: Prefira quando o volume de dados não for extremo ou
    ordenação exata por campos for essencial.

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

Prisma

ORM moderno para Node.js com suporte a cursor pagination

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

  1. Erro comum: Assumir que a ordenação pelo ID sempre é a
    desejada.
  2. Erro comum: Usar offset em bancos com milhões de registros sem
    índices otimizados.
  3. Erro comum: Implementar cursor quando navegação por página fixa
    é indispensável para o negócio.

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

  • Avaliou o formato de navegação esperado (página específica vs scroll sequencial)
  • Revisou limitações de cada modelo de paginação
  • Testou a performance em grandes volumes
  • Considerou ordenação customizada além do ID
  • Implementou fallback para mudanças de dados dinâmicos

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.

Convex — Pagination. PostgreSQL — LIMIT/OFFSET. Use The Index, Luke — No OFFSET.

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.

Continue explorando

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.

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.

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.

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ê.

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.

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.

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.