RORK LABJP
CLOUD — Rork Max compiles native Swift on a fleet of cloud Macs, so you never download Xcode or need to own a MacPLATFORM — Rork Max targets iPhone, iPad, Apple Watch, and Vision Pro, and reaches games, widgets, and Live ActivitiesSHIP — Build in the browser, preview through a streaming simulator, install on device via QR code, and submit to the App Store without leaving RorkSPLIT — Regular Rork generates cross-platform apps with React Native and Expo. Reach for it to ship broadly and fast, and for Max when you need Apple-specific depthCREDIT — The free tier works out to roughly five prompts a week. It helps to budget the cost of trying something separately from the cost of finishing itPRICE — Rork Max sits on the $200/month Max plan, while regular Rork starts free with paid plans from $25/monthCLOUD — Rork Max compiles native Swift on a fleet of cloud Macs, so you never download Xcode or need to own a MacPLATFORM — Rork Max targets iPhone, iPad, Apple Watch, and Vision Pro, and reaches games, widgets, and Live ActivitiesSHIP — Build in the browser, preview through a streaming simulator, install on device via QR code, and submit to the App Store without leaving RorkSPLIT — Regular Rork generates cross-platform apps with React Native and Expo. Reach for it to ship broadly and fast, and for Max when you need Apple-specific depthCREDIT — The free tier works out to roughly five prompts a week. It helps to budget the cost of trying something separately from the cost of finishing itPRICE — Rork Max sits on the $200/month Max plan, while regular Rork starts free with paid plans from $25/month
Articles/Dev Tools
Dev Tools/2026-06-20Advanced

Why Your Rork List Starts Duplicating and Dropping Rows as It Grows — Cursor Pagination and Resilient Refetch State

The naive offset pagination Rork scaffolds for you quietly breaks the moment your list changes underneath the user. Here is how to move to a cursor contract, fold every fetch state into one usePaginatedList hook, and recover failed page loads with exponential backoff — implementation first.

Rork525React Native218Expo156paginationinfinite scrollFlatList9state design

Premium Article

A list that felt fine at 50 rows fell apart at 3,000

As an indie developer, a list screen I run in one of my own apps slowly started misbehaving as the data grew. For the first few dozen rows everything was flawless. But once the records crossed a few thousand, the same card would appear twice mid-scroll, a handful of rows would vanish entirely, and the spinner at the bottom would sometimes turn forever.

When I traced it, I landed on the simple pagination Rork had scaffolded for me at the start: offset pagination — "give me n rows starting at position k." It works perfectly in a prototype. But on a list that keeps moving — new posts arriving, old ones removed — that approach fails silently in production.

This article unpacks why that failure happens, then rebuilds the list around a cursor contract and a single state machine for all the fetch states, in real code. Think of it as a set of notes for taking a list the AI got you started on and reworking it into something that holds up over the long run.


Why offset pagination "breaks while it's still working"

Offset pagination slices data by "starting at row k (offset), take n rows (limit)." Page one is offset=0, limit=20; page two is offset=20, limit=20.

The trouble is that the offset points at a rank. In the few seconds a user spends reading page one, suppose a new row is inserted at the top. Everything shifts back by one, and the row that should have led page two is now the same item already shown at the end of page one. That is your duplicate. Delete a row at the top instead and everything shifts forward by one, so a row gets skipped.

AspectOffsetCursor
What it points atA rank (row number)A boundary value (after this id)
List mutates mid-fetchDuplicates and gaps appearBoundary is fixed, so it stays stable
Deep-page performanceSlows down as offset growsStays roughly constant with an index
Jumping to a previous pageEasyNeeds a bidirectional cursor
Fit for infinite scrollLowHigh

For "keep reading forward" use cases like infinite scroll, carrying the boundary itself — "how far have I read?" — makes far more sense than carrying a rank. That is cursor pagination.


Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Understand why offset pagination breaks and design a cursor contract that never duplicates or skips rows while the list mutates
Fold initial load, load-more, refresh, error, and end-of-list into a single state machine inside a usePaginatedList hook
Add exponential-backoff retries for failed page fetches and id-based de-duplication for stable keys — everything a production list needs
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-30
What Renovate may bump in an Expo app, and what it must never touch
Turning on automated dependency updates in a Rork-generated app also hands Renovate the 123 packages Expo SDK 57 pins. Measured on 2026-07-30, six of them sit a full major version behind npm latest. Here is how to generate the ignore list from the SDK instead of maintaining it by hand.
Dev Tools2026-07-28
Counting what prebuild --clean will erase before you upgrade to Expo SDK 57
A raw diff between two generated ios/ trees showed 649 changed lines; only 3 were real edits. How to count what prebuild --clean erases, and move it into a config plugin.
Dev Tools2026-07-27
When to Raise Your Minimum iOS Version — Count Leftover Branches, Not User Percentages
Judging a minimum OS bump by usage share produces the same answer every year, so the decision never happens. Here is the annotation convention, the sweep script that counts how many branches each candidate floor would retire, and what to watch for 30 days after.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →