What is FlashList in React Native, and how does its cell recycling differ from FlatList's virtualization?
answer
- keep the cell, swap the item
- FlatList unmounts outside its window
- small draw distance, not viewports
- no key prop inside rows
- v2 needs the New Architecture
basics
~20 sFlashList (@shopify/flash-list) is a FlatList-compatible list that recycles: a cell scrolled off screen stays mounted and is re-rendered with a different item. FlatList instead unmounts cells outside its render window and mounts new ones as you scroll.
solid answer
~50 sBoth lists render only part of a long `data` array, but they treat off-screen rows differently. `FlatList` (a `VirtualizedList`) keeps a render window measured in viewports - `windowSize` defaults to 21 - and rows that leave it are unmounted, so scrolling keeps mounting fresh components. `FlashList` keeps a pool of mounted cells and, when one scrolls out, re-renders it with a **different `item` prop** - no unmount, no mount - with a small buffer set by `drawDistance` in pixels. Re-rendering an existing cell is cheaper than building a new one, which means less JavaScript work and fewer blank areas on fast scrolls. The price: component state, refs and a `key` prop inside the row now survive or fight recycling. The API is almost FlatList's, so migrating is mostly renaming the component, removing `key` props inside rows and checking row `useState`. FlashList v2 requires the New Architecture.
go deeper
Know that FlashList reuses mounted cells for new items while FlatList unmounts rows that leave its window, and that migrating is mostly a component rename.
Explain why re-rendering a pooled cell is cheaper than mounting a new one, and what that means for useState, key props and memoized list props.
Decide from release-build measurements whether a list needs FlashList, and make its rows recycle-safe as part of the migration rather than after the bug reports.
Set a team rule for when a list may use FlashList and what a recycle-safe row looks like, so migrations do not reintroduce stale-state bugs screen by screen.
## Two ways to keep a long list cheap A React Native marketplace grid can hold thousands of listings, and rendering every one as native views would exhaust memory and block the JavaScript thread. Both built-in `FlatList` and the FlashList library (`@shopify/flash-list`) render only what is near the viewport. They differ in what happens to a row that scrolls away. **Virtualization in FlatList.** `FlatList` is built on `VirtualizedList`. It keeps a **render window** around the viewport - `windowSize` is measured in viewport lengths and defaults to 21 - and renders spacers for the regions outside it. A row that leaves the window is **unmounted**: its component state is discarded and its native views are released. A row that enters is **mounted** from scratch. **Recycling in FlashList.** FlashList keeps a pool of mounted cells. When a cell scrolls out of view it is not destroyed; the list **re-renders the same component with a different `item` prop** and moves it to its new position. The FlashList docs put it plainly: when an item gets out of the viewport, instead of being destroyed, the component is re-rendered with a different `item`. ## Side by side | | `FlatList` | FlashList v2 | |---|---|---| | Off-screen row | unmounted, later mounted fresh | kept mounted, re-rendered with a new item | | Buffer around the viewport | `windowSize` in viewports (default 21) | `drawDistance` in pixels (default 250 on iOS and Android) | | Tuning props | `windowSize`, `initialNumToRender`, `maxToRenderPerBatch`, `getItemLayout` | none of those; sizes are measured, not estimated | | Row `useState` | starts fresh for each row | carries over to the next item unless reset | | Architecture | core React Native | New Architecture only (v2) | Re-rendering an existing component tree with new props lets React update text, images and styles in place instead of creating every view again. That is less JavaScript work per scrolled row, less memory churn, and usually fewer **blank areas** - the empty gaps a list shows when rendering cannot keep up with a fast fling. ## What recycling asks of your row component Recycling changes the contract with the row component: - **State leaks between items.** A `useState` initialised from `item` keeps the previous item's value after recycling. Reset it, or better, derive it from data. - **No `key` prop inside rows.** A `key` that changes with the item makes React treat the tree as new and remount it, which throws the recycling benefit away. When a row maps over an array, the library's `useMappingHelper` supplies recycling-friendly keys. - **Keep renders cheap.** A recycled cell re-renders on every reuse, so heavy computation in the row costs on every scroll step. - **Memoize props passed to the list.** The v2 docs say this matters more than in v1, which skipped some updates in ways developers perceived as bugs. ## Switching a list over The FlashList docs' own migration steps boil down to: 1. Change the component name from `FlatList` to `FlashList`. 2. Remove explicit `key` props from the `renderItem` hierarchy. 3. Check row components that use `useState` and decide whether that state must reset for a new item. 4. Pass `getItemType` if the list mixes very different row kinds. 5. Provide a real `keyExtractor`; v2 recommends it to avoid glitches when layouts change while scrolling upward. 6. Measure in a release build - FlashList can look slower than FlatList in development mode because of its small render buffer. ## When FlatList is still enough - **Short lists.** A settings screen or a list of 30 items never scrolls enough for recycling to matter. - **Rows that are hard to reset.** Rows full of local state, timers or nested players may cost more to make recycle-safe than the gain is worth. - **An app not yet on the New Architecture.** FlashList v2 throws at import on the legacy architecture; since React Native 0.82 the New Architecture is the only one, so this matters only for older apps. - **FlatList-only props.** FlashList has no `getItemLayout` or `windowSize`; code built around them needs rework. The rule of thumb in interviews: reach for FlashList when a long or image-heavy list shows dropped frames or blank areas in a release build, and make the rows recycle-safe as part of the switch.
- Why can FlashList look slower than FlatList during development?Because development mode makes every render much slower, and FlashList renders a deliberately small buffer around the viewport (`drawDistance`, 250 pixels by default on iOS and Android) where FlatList keeps many viewports rendered ahead. With slow dev-mode renders the small buffer runs out first. The FlashList docs say to profile only in a release build, where the comparison is meaningful.
- Does recycling mean FlashList never unmounts a row?No. Cells are pooled per item type and reused where possible, but a cell can still be unmounted - for example when the recycle pool is capped with `maxItemsInRecyclePool` (setting it to 0 disables the pool so off-screen items unmount), or when a changing `key` inside the row forces React to remount it.
A restaurant that clears and relays a table for every new party is FlatList; FlashList keeps the table laid and only swaps the name card and the plates. It is faster, as long as nobody forgets to clear the last party's leftovers - the component state that recycling carries over.
saying these in an interview costs you the question
- FlashList is FlatList with a larger default windowSize.
- Recycling means row components keep their own state per item automatically.
- Adding key={item.id} inside a FlashList row makes recycling more reliable.
- FlashList v2 still needs estimatedItemSize to lay rows out.
- FlashList helps every list, so even a 20-row settings screen should use it.