Data Pagination Implementation in Mobile Apps: Cursor vs Offset

Requesting a list of 10,000 products in one call is a classic mistake our engineers see in every third project. The API returns 40 MB of JSON, the app freezes while parsing, and the user sees a white screen for 8 seconds—then leaves. We implement pagination at the contract level between client and s

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Data Pagination Implementation in Mobile Apps: Cursor vs Offset
Medium
from 1 day to 3 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

Requesting a list of 10,000 products in one call is a classic mistake our engineers see in every third project. The API returns 40 MB of JSON, the app freezes while parsing, and the user sees a white screen for 8 seconds—then leaves. We implement pagination at the contract level between client and server: this reduces load by 3–5 times and provides smooth navigation without delays. Evaluate your API's pagination—contact us for an audit.

Pagination Implementation: Offset or Cursor?

Most developers reach for offset pagination by default: ?page=2&limit=20. It works while the data is static. But add a live feed where records are inserted at the top, and the user on page 3 misses entries or sees duplicates: an INSERT at the start shifts all offsets. On tables with 500,000+ rows, offset scans up to 100,000 rows to shift the cursor—queries take 200–400 ms.

Cursor-based pagination solves this: the server returns next_cursor, the client passes it in the next request. The cursor is an opaque token (usually base64 of id + timestamp) that fixes the position in the dataset. PostgreSQL backends implement it with WHERE id < :cursor ORDER BY id DESC LIMIT 20. No duplicates, no gaps. Query speeds remain stable—50–100 ms regardless of table size.

Criterion Offset Pagination Cursor Pagination
Implementation simplicity High Medium (requires cursor on backend)
Duplicates/missing on insert Yes No
Performance on large tables Degrades (O(N)) Stable (O(1))
Support for jumping to arbitrary page Yes (page=3) Difficult (only sequential scroll)
Caching Easy (pages by offset) Requires storing cursors

Why Cursor Pagination Is More Efficient Than Offset?

Cursor pagination is 3 times faster than offset on tables from 100,000 rows—benchmarks show response time dropping from 300 ms to 90 ms. It also guarantees data consistency under concurrent inserts. For news feeds, chat messages, and frequently updated catalogs, it's the mandatory choice.

How to Set Up Caching with RemoteMediator?

On Android with Paging 3, cursor pagination is implemented via RemoteMediator + PagingSource:

class ItemPagingSource( private val api: ItemsApi, private val query: String ) : PagingSource<String, Item>() { override suspend fun load(params: LoadParams<String>): LoadResult<String, Item> { return try { val response = api.getItems( cursor = params.key, limit = params.loadSize, query = query ) LoadResult.Page( data = response.items, prevKey = null, nextKey = response.nextCursor ) } catch (e: HttpException) { LoadResult.Error(e) } } override fun getRefreshKey(state: PagingState<String, Item>): String? { return state.anchorPosition?.let { anchor -> state.closestPageToPosition(anchor)?.nextKey } } } 

LazyColumn in Compose connects via collectAsLazyPagingItems()—a ready-made binding that handles Loading, Error, NotLoading states without extra code.

On iOS, the equivalent is a compositional layout with UICollectionViewDiffableDataSource and NSDiffableDataSourceSnapshot. Prefetch is implemented via UICollectionViewDataSourcePrefetching: the method prefetchItemsAt is called N cells before the edge, firing a network request ahead of time. Without prefetch, at scroll speeds above 500 px/s, empty cells appear—the user waits 1–2 seconds for loading.

How to Implement Cursor Pagination on Android: Step-by-Step

  1. Define the API contract: the server returns next_cursor in the JSON response.
  2. Create PagingSource<String, Item> that passes the cursor in loadParams.key.
  3. Connect RemoteMediator for offline cache: RemoteMediator fetches data from the network and saves it to Room.
  4. In PagingSource, implement getRefreshKey to restore position after a refresh.
  5. Set up LazyColumn with collectAsLazyPagingItems() and an explicit LoadStateFooter with a retry button.

Caching Strategy Comparison: RemoteMediator vs. Simple Cache

Parameter RemoteMediator + Room Simple Cache (LruCache)
Offline access Yes No
Consistency on updates ETag/Last-Modified No mechanism
Implementation complexity Medium Low
Traffic savings 60–80% 0%
Recommendation High-load projects Prototypes, static data

Infinite Scroll and Pull-to-Refresh

Infinite scroll without an error state is a typical problem. The network drops on page 5, request hangs, user scrolls down and nothing happens. An explicit LoadStateFooter with a retry button is needed.

In Paging 3:

adapter.addLoadStateListener { loadState -> binding.retryButton.isVisible = loadState.source.append is LoadState.Error binding.progressBar.isVisible = loadState.source.append is LoadState.Loading } 

Pull-to-refresh resets pagination to the first page via adapter.refresh()—Paging 3 invalidates PagingSource and starts fresh. On SwiftUI, it's the refreshable modifier that calls invalidateQueries() in TCA or updates the @StateObject view model.

Cache and Offline

Paging 3 + Room is the standard offline-first combo. RemoteMediator writes data to the local DB, PagingSource reads from Room rather than the network. The user sees data even without internet; fresh data loads in the background.

Key point: cache invalidation strategy. If the server updates a record, the local copy becomes stale. Solution: ETag or Last-Modified in response headers—the client sends If-None-Match, server returns 304 with no body if unchanged. Room updates only changed records via @Insert(onConflict = OnConflictStrategy.REPLACE). This reduces downloaded data by 60–80%.

What's Included in the Work

  • Audit of the current API and pagination contract specification.
  • Implementation of cursor/offset pagination on the client (Android/iOS/cross-platform).
  • Integration of RemoteMediator and Room for offline mode.
  • Configuration of prefetch and retry logic.
  • Testing on real devices with poor network simulation.
  • Code documentation and backend improvement recommendations.
  • Post-implementation support: 2 weeks of consultation.

Timelines

Simple offset pagination without cache—1–2 days. Cursor pagination with RemoteMediator, offline cache, and retry logic—3–5 days. The cost is calculated individually after analyzing requirements and the existing API. We have 5+ years of experience in mobile development and over 30 projects with pagination—trust our specialists. Order a pagination audit of your app—we'll find the optimal solution.

Typical Pagination Implementation Mistakes

  • Missing prefetch on iOS—empty cells on fast scroll.
  • Using offset in feeds with frequent inserts—duplicates and gaps.
  • Ignoring loading and error states: user sees no indicator.
  • No caching—every scroll triggers a network request.
  • Wrong page size: 5 items causes frequent loads, 100 causes long waits.