Journal

Build Infinite Scroll in React Native with TanStack Query

Build Infinite Scroll in React Native with TanStack Query: a reflective 3D App Store icon on a blue and purple gradient

TL;DR

Infinite scroll in React Native is a split: TanStack Query's useInfiniteQuery owns the data, paging, caching, and fetching the next page, while a virtualized list like FlashList owns the rendering and tells the query when to load more. You flatten the pages into one list, trigger fetchNextPage before the user hits the bottom, and show honest loading, error, and end-of-list states. Start from an infinite-scroll template so the query wiring and the list states are already shaped.

Infinite scroll is two systems, not one

Infinite scroll looks like one feature and is really two systems cooperating. One owns the data: fetching pages, caching them, knowing whether more exist, and loading the next page on demand. The other owns the rendering: showing the rows efficiently and telling the data side when the user is near the end. Treat them as one blob and you get the classic bugs, double fetches, lost scroll position, a spinner that never ends. Keep them separate and each is simple.

TanStack Query, with more than 54,000,000 weekly downloads, is the data side. A virtualized list is the rendering side. The art is the handshake between them.

The data side: useInfiniteQuery and cursors

On the data side, useInfiniteQuery is built for exactly this. You give it a function that fetches one page and a getNextPageParam that reads the last page and returns the cursor for the next one, or undefined when there are no more. It accumulates pages in its cache, exposes fetchNextPage, and tracks hasNextPage and isFetchingNextPage so your UI always knows the state. Prefer cursor pagination, a token pointing at the next item, over offset pagination, because offsets drift and duplicate rows when the underlying list changes between fetches.

You then flatten the pages into a single array for the list. The query keeps the page structure; your list wants one flat sequence, and that mapping is a one-liner you do in render.

The list side: trigger before the bottom

On the rendering side, a virtualized list like FlashList or a tuned FlatList shows the flattened rows and fires an end-reached callback as the user nears the bottom. The detail that separates smooth from janky is the threshold: trigger fetchNextPage when the user is a screen or so from the end, not at the very last pixel, so the next page is already arriving before they would otherwise hit empty space. Guard the trigger with hasNextPage and isFetchingNextPage so a fast scroll does not fire three overlapping fetches for the same page.

That guard is the single most important line. Without it, end-reached fires repeatedly and you load the same page several times.

The states that make it feel finished

Infinite scroll feels finished when its states are honest. A footer spinner shows while the next page loads, so the user knows more is coming. An end-of-list marker, a quiet “you are all caught up,” appears when hasNextPage is false, rather than spinning forever at the bottom. An error on a page load shows a retry affordance in the footer instead of silently stopping. And pull-to-refresh resets the query to the first page. The same virtualized-list discipline carries a travel-history timeline, and the per-row performance habits mirror fixing FlatList memory and lag.

Building it from a template

The query wiring, the threshold trigger, the footer spinner, the end marker, and the error retry are the same in every infinite list, so they are worth starting from. A free VP0 design ships the infinite-scroll list with its loading, error, and end states and a machine-readable source page, so pasting the link into Claude Code or Cursor gives the agent the list and the state model to wire to your useInfiniteQuery. The grid variant of the same pattern sits behind a Pinterest-style masonry grid.

Common mistakes building infinite scroll

The frequent ones live in the handshake. Firing the fetch only at the very bottom produces a visible stall before more rows appear. Not guarding the trigger with hasNextPage and isFetchingNextPage loads the same page several times. Using offset pagination on a changing list duplicates and skips rows. Forgetting the end-of-list state leaves a spinner running forever after the last page. And rendering an un-virtualized list re-mounts everything and leaks memory as the list grows.

Key takeaways: infinite scroll in React Native

  • It is two systems. TanStack Query owns the data, a virtualized list owns the rendering.
  • Use useInfiniteQuery with cursors. getNextPageParam and cursor pagination beat fragile offsets.
  • Trigger before the bottom, and guard it. Fetch a screen early, gated by hasNextPage and isFetchingNextPage.
  • The states make it feel finished. Footer spinner, end-of-list marker, error retry, pull-to-refresh.
  • Start from an infinite-scroll template. A free VP0 design gives an agent the list and states to wire to the query.

Frequently asked questions

How do I build infinite scroll in React Native with TanStack Query? Split it into data and rendering. Use useInfiniteQuery with a getNextPageParam that returns the next cursor, or undefined when there are no more pages, and flatten the cached pages into one array. Render that array in a virtualized list like FlashList, and fire fetchNextPage from the end-reached callback a screen before the bottom, guarded by hasNextPage and isFetchingNextPage so it does not double-fetch. Add a footer spinner, an end-of-list marker, and an error retry. A free template gives you the list and the states to start from.

What is the safest way to build this with Claude Code or Cursor? Give the agent an infinite-scroll template with the states already shaped, and let it wire your useInfiniteQuery into it. A free VP0 React Native design has a machine-readable source page with the list, the footer loading state, the end-of-list marker, and the error retry, so Claude Code or Cursor connects your paged API to a working list. That avoids the common result where an AI tool fires the fetch at the very bottom with no guard and triggers overlapping page loads.

Can VP0 provide a free React Native template for an infinite list? Yes. VP0 has free infinite-scroll designs in React Native with the list, the footer spinner, the end-of-list marker, and the error and empty states already built, each exposing an AI-readable source page. Because the list exists, your agent connects it to your useInfiniteQuery instead of reinventing the threshold trigger and the state handling that usually trip up hand-built infinite scroll.

Should I use cursor or offset pagination for infinite scroll? Cursor pagination, in almost all cases. A cursor is a token pointing at the next item, so it stays correct even when the underlying list changes between fetches, while offset pagination drifts and produces duplicate or skipped rows when items are added or removed during scrolling. useInfiniteQuery supports either through getNextPageParam, but returning a cursor from the last page is the more reliable pattern for a live feed.

What common errors happen when vibe coding infinite scroll? Firing the fetch only at the very bottom so the list visibly stalls, and not guarding the trigger so it double-fetches the same page, are the frequent ones. Offset pagination on a changing list duplicates and skips rows, forgetting the end-of-list state spins forever after the last page, and an un-virtualized list leaks memory as it grows. Trigger early, guard with hasNextPage and isFetchingNextPage, use cursors, and handle the end and error states.

What VP0 builders also ask

How do I build infinite scroll in React Native with TanStack Query?

Split it into data and rendering. Use useInfiniteQuery with a getNextPageParam that returns the next cursor, or undefined when there are no more pages, and flatten the cached pages into one array. Render that array in a virtualized list like FlashList, and fire fetchNextPage from the end-reached callback a screen before the bottom, guarded by hasNextPage and isFetchingNextPage so it does not double-fetch. Add a footer spinner, an end-of-list marker, and an error retry. A free template gives you the list and the states to start from.

What is the safest way to build this with Claude Code or Cursor?

Give the agent an infinite-scroll template with the states already shaped, and let it wire your useInfiniteQuery into it. A free VP0 React Native design has a machine-readable source page with the list, the footer loading state, the end-of-list marker, and the error retry, so Claude Code or Cursor connects your paged API to a working list. That avoids the common result where an AI tool fires the fetch at the very bottom with no guard and triggers overlapping page loads.

Can VP0 provide a free React Native template for an infinite list?

Yes. VP0 has free infinite-scroll designs in React Native with the list, the footer spinner, the end-of-list marker, and the error and empty states already built, each exposing an AI-readable source page. Because the list exists, your agent connects it to your useInfiniteQuery instead of reinventing the threshold trigger and the state handling that usually trip up hand-built infinite scroll.

Should I use cursor or offset pagination for infinite scroll?

Cursor pagination, in almost all cases. A cursor is a token pointing at the next item, so it stays correct even when the underlying list changes between fetches, while offset pagination drifts and produces duplicate or skipped rows when items are added or removed during scrolling. useInfiniteQuery supports either through getNextPageParam, but returning a cursor from the last page is the more reliable pattern for a live feed.

What common errors happen when vibe coding infinite scroll?

Firing the fetch only at the very bottom so the list visibly stalls, and not guarding the trigger so it double-fetches the same page, are the frequent ones. Offset pagination on a changing list duplicates and skips rows, forgetting the end-of-list state spins forever after the last page, and an un-virtualized list leaks memory as it grows. Trigger early, guard with hasNextPage and isFetchingNextPage, use cursors, and handle the end and error states.

Part of the React Native & Expo: Mobile Frontend Architecture hub. Browse all VP0 topics →

Keep reading

Build an NS Flex Travel History Timeline in React Native: a glossy App Store icon on a blue, pink and orange gradient with bubbles
Guides 7 min read

Build an NS Flex Travel History Timeline in React Native

A travel history timeline lists past journeys by date. Here is how to build the NS Flex trip-history screen in React Native with fast scrolling and offline cache.

Lawrence Arya · June 8, 2026
Build a Responsive iPhone-to-iPad Layout in React Native: the App Store logo as a frosted glass icon on a pink and blue gradient with bubbles
Guides 8 min read

Build a Responsive iPhone-to-iPad Layout in React Native

A responsive tablet layout changes shape, it does not just scale up. Here is how to build an adaptive iPhone-to-iPad layout in React Native with breakpoints.

Lawrence Arya · June 9, 2026
Build a High-Performance Candlestick Chart in React Native: a reflective 3D App Store icon on a blue and purple gradient
Guides 8 min read

Build a High-Performance Candlestick Chart in React Native

A candlestick chart with thousands of candles and smooth pan-zoom needs Skia, not SVG. Here is how to build a high-performance candlestick chart in React Native.

Lawrence Arya · June 8, 2026
Build a Custom Screen Time Chart UI in React Native: a glass app tile showing the VP0 logo on a pink and blue gradient
Guides 6 min read

Build a Custom Screen Time Chart UI in React Native

A custom screen time chart has two parts: the usage data and the chart. Here is how to build the screen time chart UI in React Native, data limits and all.

Lawrence Arya · June 8, 2026
Build a Free Sendbird-Style Chat UI in React Native: a glass iPhone app-grid icon on a mint and teal gradient
Guides 6 min read

Build a Free Sendbird-Style Chat UI in React Native

Sendbird's chat UI kit is tied to its backend. Here is how to build the same React Native chat screens, channel list, message bubbles, and composer, for free.

Lawrence Arya · June 8, 2026
Build a Twitch-Style Chat Overlay for React Native Video: the App Store logo as a glossy glass icon on a purple and blue gradient with floating bubbles
Guides 8 min read

Build a Twitch-Style Chat Overlay for React Native Video

A Twitch-style chat overlay has to stay smooth while a busy chat floods over live video. Here is how to build the chat overlay in React Native without lag.

Lawrence Arya · June 8, 2026