FlatList vs FlashList vs LegendList
Rendering lists is one of the most common performance bottlenecks in React Native. Feeds, chats, activity logs, search results—almost every app ends up with long, dynamic lists.
React Native gives you FlatList by default. Shopify introduced FlashList to fix performance issues at scale. And now LegendList pushes things even further.
Here's how they compare—and why the default choice should change.
FlatList
What It Is
FlatList is the built-in list component that ships with React Native. It uses virtualization to render only visible items plus a buffer.
Where It Works Well
FlatList is fine for:
- Short lists (under 50 items)
- Simple row components
- Low-frequency updates
- Prototypes and MVPs
For basic use cases, FlatList does the job.
Where It Breaks Down
As lists grow or get more complex, FlatList shows its limitations:
Frequent mount/unmount during scroll. Items are constantly created and destroyed as you scroll, which triggers garbage collection and causes jank.
Poor performance with complex layouts. If your list items have nested components, images, or dynamic heights, FlatList struggles to keep scrolling smooth.
Low-end Android devices suffer. The constant recycling hits older phones hard. Frame drops are common.
Requires heavy tuning. To make FlatList performant at scale, you need to configure getItemLayout, maxToRenderPerBatch, windowSize, memoize components, and carefully manage re-renders. It becomes a full-time job.
Reality
FlatList is fine for demos and small apps, but it becomes a liability once lists grow or layouts get more complex.
If you're building a production app with a real feed, you'll outgrow FlatList quickly.
FlashList
What It Is
FlashList is a high-performance replacement created by Shopify. It improves scrolling smoothness by recycling views instead of constantly tearing them down.
Why It's Better Than FlatList
View recycling reduces GC pressure. Instead of creating new components for every item as you scroll, FlashList reuses existing ones. This dramatically reduces garbage collection overhead.
Far fewer dropped frames. Scrolling is noticeably smoother, especially on mid-range and low-end Android devices.
Designed for large feeds. FlashList was built to handle Shopify's product catalogs, which can have thousands of items.
Easy migration. FlashList's API is intentionally similar to FlatList, so switching is usually just a drop-in replacement:
// Before
import { FlatList } from 'react-native';
<FlatList
data={items}
renderItem={({ item }) => <ItemCard item={item} />}
keyExtractor={item => item.id}
/>
// After
import { FlashList } from '@shopify/flash-list';
<FlashList
data={items}
renderItem={({ item }) => <ItemCard item={item} />}
keyExtractor={item => item.id}
estimatedItemSize={100} // Add this for best performance
/>
Limitations
FlashList isn't perfect:
Still needs tuning for dynamic item sizes. If your items vary wildly in height, you need to provide accurate estimatedItemSize or performance degrades.
Optimized mainly for predictable layouts. FlashList works best when items are similar in structure. Complex, heterogeneous lists (like Twitter feeds with text, images, videos, polls) require more configuration.
Another abstraction you may eventually outgrow. For most apps, FlashList is the ceiling. But if you're building something extremely dynamic, you might need more.
Reality
FlashList is a big upgrade and should already replace FlatList in most production apps—but it's not the final form.
If you're starting a new app today and choosing between FlatList and FlashList, choose FlashList. It's objectively better with minimal downsides.
LegendList
What It Is
LegendList is a newer list implementation that goes beyond FlashList by focusing on dynamic data, unknown sizes, and real-world app behavior.
Why It Stands Out
True item recycling with fewer assumptions. LegendList recycles views like FlashList but handles edge cases better. It doesn't assume your items are uniform.
Handles highly dynamic item heights better. If your list has text posts, images, videos, and polls all mixed together (like a social feed), LegendList adapts without heavy configuration.
Built-in support for chat-like and feed-like UIs. Scrolling from the bottom, inserting items at the top, real-time updates—LegendList is designed for these patterns.
Designed for constant updates, inserts, and deletes. Most apps don't have static lists. Items appear, disappear, and change. LegendList handles this smoothly.
Minimal configuration needed. Unlike FlatList (which requires tons of props to tune) or FlashList (which needs accurate item size estimates), LegendList works well out of the box.
What This Means in Practice
You spend less time fighting the list and more time building the product.
If you're building a chat app, a social feed, or an activity log, LegendList handles the complexity for you. No endless tweaking of props. No profiling to figure out why scrolling drops frames.
Reality
LegendList matches how modern apps actually behave—messy data, variable layouts, infinite scrolling, and real-time updates.
It's built for the real world, not the ideal case.
Side-by-Side Summary
| Feature | FlatList | FlashList | LegendList | | --- | --- | --- | --- | | Virtualization | ✅ | ✅ | ✅ | | View recycling | ❌ | ✅ | ✅ (stronger) | | Dynamic item sizes | ⚠️ | ⚠️ | ✅ | | Performance at scale | ❌ | ✅ | ✅✅ | | Configuration overhead | High | Medium | Low | | Future-proofing | ❌ | ⚠️ | ✅ |
The Updated Take
LegendList should be the default choice for most React Native apps.
Here's why:
Most apps are feeds, logs, or lists with dynamic content. Not static, uniform catalogs. LegendList is built for this.
Most lists change size, order, and content frequently. Real-time data, user interactions, infinite scroll. LegendList handles it better.
Most teams don't want to micro-optimize list props for weeks. FlatList requires constant tuning. FlashList needs accurate size estimates. LegendList works out of the box.
Most performance bugs show up after launch. When you have real users, real data, and real edge cases, LegendList's handling of dynamic content saves you.
LegendList handles these realities better.
FlatList is legacy. It was fine when it was the only option. But we have better tools now.
FlashList is a strong middle step. It's a huge improvement over FlatList and still the right choice for many apps. If you're already using it and it works, no need to switch.
LegendList is where things are heading. It's the most modern, most adaptive, most real-world solution. If you're starting a new app today, choosing anything else means you're accepting limitations you don't need to accept.
Practical Recommendation
New app → Start with LegendList
Unless you have a specific reason not to, LegendList should be your default. It handles the widest range of use cases with the least configuration.
Existing FlatList app → Migrate to LegendList if you've hit performance issues
If your FlatList is causing frame drops, jank, or you're spending hours tuning props, switch to LegendList.
FlashList app → Stay if it works, but know there's a better ceiling
If FlashList is meeting your needs, don't fix what isn't broken. But if you're adding more complex features (real-time updates, variable content, chat-like behavior), LegendList will save you pain.
Migration Examples
From FlatList to LegendList
// Before (FlatList)
import { FlatList } from 'react-native';
<FlatList
data={posts}
renderItem={({ item }) => <PostCard post={item} />}
keyExtractor={item => item.id}
getItemLayout={(data, index) => ({
length: 200,
offset: 200 * index,
index,
})}
maxToRenderPerBatch={5}
windowSize={5}
/>
// After (LegendList)
import { LegendList } from '@legendapp/list';
<LegendList
data={posts}
renderItem={({ item }) => <PostCard post={item} />}
keyExtractor={item => item.id}
/>
That's it. No more guessing at item heights. No more tuning render batches.
From FlashList to LegendList
// Before (FlashList)
import { FlashList } from '@shopify/flash-list';
<FlashList
data={posts}
renderItem={({ item }) => <PostCard post={item} />}
keyExtractor={item => item.id}
estimatedItemSize={200}
/>
// After (LegendList)
import { LegendList } from '@legendapp/list';
<LegendList
data={posts}
renderItem={({ item }) => <PostCard post={item} />}
keyExtractor={item => item.id}
/>
Even simpler. No need to estimate item sizes.
Real-World Performance
Here's what you'll notice after switching to LegendList:
Smoother scrolling. Fewer dropped frames, especially with mixed content types.
Faster initial render. Lists with hundreds of items appear faster.
Better real-time updates. Adding or removing items doesn't cause visible jank.
Less time debugging. Configuration is minimal, so you spend less time tweaking props and more time building features.
When FlatList or FlashList Still Make Sense
FlatList:
- Quick prototypes where performance doesn't matter yet
- Very short lists (under 20 items)
- You're constrained to React Native core libraries only
FlashList:
- Catalog-style apps with uniform items (e-commerce product grids)
- You're already using it and it works well
- You need battle-tested, widely adopted libraries
LegendList:
- Everything else
Conclusion
Performance should be boring. Your list should just work.
That's the bar LegendList sets.
FlatList was the foundation. FlashList was the evolution. LegendList is the current state of the art.
If you're starting a new app in 2025, there's no reason to choose worse performance, more configuration, and less flexibility.
Use LegendList. Ship fast. Move on to building features that matter.
Learn More: