Skip to content
All posts
August 3, 20264 min read

Paging 3 in Android: Efficient List Loading at Scale

Loading a thousand-item list all at once is how apps run out of memory and feel sluggish. Paging 3 loads data in chunks as the user scrolls. Here's how it fits together with Compose, Room, and a network source.

Paging 3AndroidJetpack ComposePerformanceKotlin
Share:

The naive way to show a long list is to load everything into memory and hand it to the UI. It works for fifty items and falls apart at five thousand — memory balloons, the initial load drags, and scrolling stutters. Paging 3 is Jetpack's answer: it loads data in pages as the user scrolls, keeps memory bounded, and handles the fiddly states — loading, error, retry, end-of-list — that you'd otherwise hand-roll badly. It has a few moving parts, but they click together cleanly.

The Core Pieces

Paging 3 centers on three things. A

code
PagingSource
knows how to load one page given a key. A
code
Pager
configures paging and produces a stream of
code
PagingData
. And in the UI, you collect that stream and feed it to a lazy list. The ViewModel ties them together.

kotlin
val items: Flow<PagingData<Item>> = Pager(
    config = PagingConfig(pageSize = 20),
) {
    ItemPagingSource(api)
}.flow.cachedIn(viewModelScope)

The

code
cachedIn(viewModelScope)
is important — it caches the paged data across configuration changes so a rotation doesn't reload everything from scratch.

Wiring It Into Compose

On the UI side, Paging has first-class Compose support. You collect the paging data and drive a

code
LazyColumn
from it, and the library hands you the load states so you can show spinners and errors in the right places.

kotlin
val items = viewModel.items.collectAsLazyPagingItems()

LazyColumn {
    items(items.itemCount) { index ->
        items[index]?.let { ItemRow(it) }
    }
}

As the user scrolls near the end of what's loaded, Paging requests the next page automatically. You don't write scroll listeners or threshold math — it watches access patterns and prefetches for you.

Handle the Load States

The part people skip and regret is load-state handling. A paged list has more states than a simple one: the initial load, appending the next page, refreshing, and the error variants of each. Paging exposes all of them through

code
loadState
, and a polished list reflects each — a full-screen spinner on initial load, a footer spinner while appending, an inline retry when a page fails. Handling these is what separates a list that feels solid from one that flashes empty or silently stalls when the network hiccups. It's worth the few extra
code
when
branches.

Combine Network and Database With RemoteMediator

For an offline-capable app, the strong pattern is paging from the local database and using a

code
RemoteMediator
to fill that database from the network as needed. The UI always reads from Room, so it works offline and shows cached data instantly, while the mediator fetches more pages from the API in the background and writes them to the database. This single-source-of-truth approach — UI reads the database, network feeds the database — is more setup than paging straight from the network, but it gives you offline support and a snappy experience for free, which is usually worth it for any list users return to.

Don't Reach for It Too Soon

A closing caution in the spirit of keeping things simple: Paging 3 is the right tool for genuinely large or unbounded lists, and it's overkill for a list of twenty items you already have in memory. The configuration and load-state handling are real overhead, and applying them to a short, fixed list is complexity with no payoff. I reach for Paging when the data is large enough that loading it all at once is a genuine memory or performance problem, and I use a plain

code
LazyColumn
over an in-memory list otherwise. Matching the tool to the actual scale of the data is the difference between Paging earning its keep and just adding ceremony.

The reason Paging 3 has more moving parts than a plain list is that it's solving genuinely harder problems — bounded memory, prefetching, the full matrix of load and error states, and optionally an offline cache — that you'd otherwise have to solve yourself, badly. When the data is large enough to need those solutions, having them handed to you in a coherent library is a real gift, and the setup cost pays for itself the first time the network fails mid-scroll and your list degrades gracefully instead of breaking. When the data is small, none of those problems exist, and the library is answering questions nobody asked. So the whole decision reduces to one honest look at your data's scale, which is exactly the kind of judgment that keeps a codebase free of complexity it doesn't need.

Key Takeaways

  • Paging 3 loads data in pages as the user scrolls, keeping memory bounded and the initial load fast.
  • The core is a
    code
    PagingSource
    , a
    code
    Pager
    producing a
    code
    PagingData
    flow, and
    code
    cachedIn
    to survive configuration changes.
  • Use
    code
    collectAsLazyPagingItems
    with a
    code
    LazyColumn
    ; Paging prefetches the next page automatically, no scroll listeners.
  • Handle every load state — initial, append, refresh, and their errors — for a list that feels solid under bad networks.
  • Page from Room with a
    code
    RemoteMediator
    filling it from the network to get offline support and instant cached data.
  • Reserve Paging for genuinely large lists; a plain
    code
    LazyColumn
    is simpler for short in-memory ones.
Share:
S

Sudarshan Chaudhari

AI Systems Builder / Product Engineer

Bangkok, Thailand

Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.

Stay updated

Get new posts on Android, Kotlin, and solo dev straight to your inbox.

Newsletter preferences

Related Apps

MyFamilyTracker

Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.

Building something? Available for Android dev and QA consulting.

Work with me

Comments — powered by Giscus

Apps tagged with this