react-brain
Browse decisions
React & language foundations 3App architecture 9UI 12Platform & native 9Build, test, observe, secure 6AI in React apps 3stack composerdecision recordscensusstaleness bencharchitecturechangelogmethodologyroadmap

entriesui · verified 2026-07-10 · react + react-native

Lists & virtualization

reviewedconfidence: hightrack: weakenedthis tier holds 75% on the public scorecard (6/11 graded · 0 overturned) →

re-verified 3× — 2026-07-13 · 2026-07-10 · 2026-07-06 · changelog

recommendation

RN: FlashList for large lists, FlatList otherwise, ScrollView only for <20 static items. Web: react-virtual / react-window.

  • huge web tables (100k+ rows) → HighTable
  • one list engine across RN + web → Legend List
  • old-architecture RN app → FlashList v2 is New-Arch-only; pin FlashList v1.x or stay on FlatList
  • exact thresholds / perf → react-native-best-practices owns the rules

Options & tradeoffs

the field considered — and why each one isn’t the default here

optiontradeoffevidence
ScrollView/.mapfine <20 static items only
FlatListRN baseline virtualization
FlashListrecycling; large RN lists (v2 = New Architecture ONLY; old arch → pin v1.x)1.4M/wk · ships in 8/34
react-window / react-virtual / HighTableweb virtualization; HighTable handles billion-row tables (viewport-only DOM + scrollbar downscaling)6.3M/wk · ships in 5/34
Legend Listone virtualization engine for RN + web (v3.0 stable); SectionList + chat/anchoring APIs; signal-driven container pool → very small re-renders265k/wk · ships in 2/34

evidence: npm weekly downloads (signals snapshot) · “ships in n/D” = adoption across the production-app census, honest denominators

Migration lines

version and deprecation lines this decision tracks — for YOUR repo's sequenced plan: the doctor or npx -y @heart-it/react-brain migrate .

@shopify/flash-list < 2.0.0 → FlashList v2 [upgrade · effort M]

v2 is a ground-up New-Architecture rewrite (recycling, progressive rendering, pre-paint correction) and is New-Arch-ONLY — on the old architecture stay pinned to v1.x until the RN ladder is done

shopify.engineering/flashlist-v2

npm weekly downloads (from the corpus's last signals run): @shopify/flash-list 1.4M · @legendapp/list 265k · react-window 6.3M · @tanstack/react-virtual 14.7M · @tanstack/react-table 15.9M

Canonical reading

Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.

How to Build the Fastest Apps: Break the Rules (App.js Conf 2026)Jay Meistrich — author of Legend List

Why Legend List is fast, from its author: it mounts a fixed pool of absolutely-positioned containers once and never re-renders that array again — instead it *signals* individual containers to re-render themselves at a new position/item as you scroll, and turns frequent size changes into animated-style updates rather than list re-renders. The generalizable lesson ('render once'): keep each render tiny by pushing it to the leaf that actually changed. Complements the peterp.me engine walkthrough with the same-source mechanism; announces v3.0 stable (RN + web). Vendor talk.

FlashList v2: a ground-up rewrite for React Native's New ArchitectureTalha Naqvi (Shopify Engineering)

The maintainer's architecture deep-dive on FlashList's New-Architecture rewrite — view recycling, progressive rendering, layout prediction, and pre-paint correction via synchronous measurement — explaining the recycling machinery from first principles, not just the API.

React windowing vs. component recyclingIskander Samatov (LogRocket)

Cleanly separates the two long-list strategies — windowing (render only the viewport, mount/unmount on scroll) vs recycling (reuse nodes by reassigning keys) — and when to escalate. Bridges web (react-window) and RN (FlashList).

What's actually happening inside Legend ListPeter Piekarczyk

Explains Legend List's virtualization formula — estimated item size, draw distance, container-pool ratio, asymmetric 1.5x/0.5x buffering. A concrete mental model for list recycling performance (complements the FlashList piece).

Virtual scrolling for billions of rows (techniques from HighTable)Sylvain Lesage

The five techniques behind HighTable — lazy slicing, the ~17M-px canvas-height ceiling with downscaled scrollbars, dual local/global scroll modes, decoupled axes. The web huge-table counterpart to the RN list pieces above (HighTable is a listed option here).

Sources

Depth (in-domain rules) is owned by the react-native-best-practices skill — this entry is selection breadth.

The full explanation

The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.

About lists & virtualization in React & React Native

Diataxis: Explanation. This page builds understanding of why long lists need virtualization and which strategy each tool uses. It is not a tutorial, and the exact thresholds (when FlashList, item-size rules) are owned by react-native-best-practices. Read this for the why.

The one fact that organises everything: cost scales with mounted nodes, not data length

A list is cheap or expensive based on how many view/DOM nodes it mounts, not how many items the array holds. Render 10,000 rows naively and you create 10,000 nodes — layout, memory, and scroll all collapse. The whole field exists to break that coupling: render only what's on (or near) the screen. Two strategies do it, and every library here is one of them:

  • Windowing — mount only the items in the viewport (plus a small buffer); as you scroll, unmount what leaves and mount what enters. The node count stays ~constant regardless of data size. (FlatList, react-window, react-virtual.)
  • Recycling — keep a small fixed pool of views and re-bind them to new data as they scroll in/out, instead of unmounting and remounting. Cheaper still for big, uniform lists because it avoids mount/unmount churn. (FlashList.)

So the first question is never "which list lib" — it's "how many nodes will this mount, and is windowing enough or do I need recycling?"

The default, and why

React Native: FlashList for large lists, FlatList otherwise, ScrollView only for <~20 static items. Web: react-virtual / react-window.

The RN ladder is about matching strategy to scale. ScrollView mounts everything — fine for a handful of static rows, a foot-gun beyond that. FlatList adds windowing — the correct baseline for most lists. FlashList adds recycling — the win for large lists, where mount/unmount churn dominates (its v2 is a New-Architecture rewrite; see the reading). On the web, the data isn't native views but DOM nodes, and the same logic applies: react-window / react-virtual window the viewport.

The landscape, and when each one wins

ScrollView / .map() — no virtualization; every item mounts. Correct only for small, static lists (<~20). Using it for real data is the most common list mistake.

FlatList — RN's built-in windowing. The right default; tune-able, mature, and enough for the majority of lists.

FlashList — recycling for large RN lists. Wins when lists are long and rows are uniform enough to reuse; v2's ground-up New-Architecture rewrite handles recycling, progressive rendering, and layout prediction (reading). Reach for it at scale, not reflexively. Note that v2 runs only on the New Architecture — old-arch apps must pin v1.x (or stay on FlatList).

react-window / react-virtual / HighTable (web) — web windowing. react-window/react-virtual cover normal long lists; HighTable handles billion-row tables with tricks the others don't (working around the ~17M-px canvas-height ceiling with downscaled scrollbars, decoupled axes — reading). Reach for HighTable specifically for huge tabular data.

Legend List — one virtualization engine across RN + web (v3 adds SectionList + chat APIs). The pick when you want a single list mental model on both platforms.

Tradeoffs and failure modes to name out loud

  • ScrollView for real data. The cardinal sin: it mounts everything, so it's smooth at 15 rows and janks at 200. Match the tool to the count.
  • FlashList reflexively. Recycling shines for large, uniform lists; for short or wildly heterogeneous lists, FlatList's windowing is simpler and fine. Don't reach for recycling before you have the scale that needs it.
  • Fighting recycling with unstable items. Recycled rows assume reuse; per-row unstable state, unkeyed content, or wildly variable heights undercut the pool and reintroduce churn.
  • Ignoring the thresholds. "How many items before FlashList," item-size estimates, and render-perf rules are exact and owned by react-native-best-practices — this page gives the model, that skill gives the numbers.

How it interacts with the rest of the stack

  • Charts/graphics (RB-E-CHARTS). Huge-data rendering shares DNA — at extreme scale (charts, billion-row tables) the answer shifts from DOM/view virtualization toward canvas/GPU rendering.
  • Data (RB-E-DATA). Long lists usually mean paginated/infinite server data; the list's windowing pairs with the data layer's useInfiniteQuery-style fetching.
  • Perf depth (react-native-best-practices). Exact thresholds, item-layout, and the RN-RULE-USE-FLASHLIST guidance live there.

In one paragraph

A list's cost is the nodes it mounts, not the data length — so render only what's on screen, via windowing (mount the viewport, unmount the rest) or recycling (reuse a fixed view pool). On React Native: ScrollView only for tiny static lists, FlatList (windowing) as the baseline, FlashList (recycling) at scale. On the web: react-window/react-virtual for normal lists, HighTable for billion-row tables; Legend List when you want one engine across both. Don't use ScrollView for real data, don't reach for recycling before you need it, and get exact thresholds from react-native-best-practices.


See also: RB-E-CHARTS (canvas/GPU rendering at extreme data scale), RB-E-DATA (paginated / infinite data behind long lists). Exact thresholds + render-perf rules: the react-native-best-practices skill.

Related in ui: styling · component-libs · animation · a11y · i18n · charts · editors · svg · maps · calendars · polish