skip to content

In a React Native FlatList, what does removeClippedSubviews do, what is its default per platform, and why can it hide content?

level: middleimportance: should knowfreq 32%

answer

  1. native side, not JavaScript side
  2. detached is not deallocated
  3. different on Android and iOS
  4. transforms and absolute positioning

basics

~20 s

removeClippedSubviews detaches native views outside the viewport from the native hierarchy to cut drawing work. FlatList defaults it to true on Android and false on iOS; transforms or absolute positioning can make visible content disappear.

solid answer

~40 s

`removeClippedSubviews` works on the native side, inside the render window: native views outside the scroll view's visible bounds are detached from the view hierarchy, so they skip drawing traversals on the main thread. It is different from windowing, which unmounts React rows outside the window in JavaScript. The views are detached, not deallocated, so it saves little memory. In React Native 0.87, `FlatList` defaults it to `true` on Android and `false` on iOS. The docs warn it can produce missing content, mainly on iOS, when rows use transforms or absolute positioning, because the native side judges visibility from a view's frame, which may not match where the content is drawn.

go deeper

for a junior

Recall that removeClippedSubviews exists on FlatList, that it hides off-screen native views, and that it is on by default only on Android.

for a middle

Explain how it differs from windowing: native views detached inside the window versus React rows unmounted outside it, and why memory barely changes.

for a senior

Diagnose missing rows on iOS by toggling the prop, and tie the bug to transforms or absolute positioning drawing content outside a view's frame.

for a principal

Decide per list whether the drawing savings justify the risk, and set a testing rule for animated or overlapping rows before enabling it on iOS.

## Two different kinds of "off screen" A React Native `FlatList` has two separate mechanisms for dealing with content the user cannot see, and interviewers like to check that candidates can tell them apart: - **Windowing** is done in JavaScript by `VirtualizedList`. Rows outside the render window (by default the viewport plus up to ten viewport lengths either side) are **unmounted**: their React components are gone and spacer views take their place. - **`removeClippedSubviews`** acts on the **native** side, inside the window. When it is `true`, native child views that are outside the scroll view's visible bounds are **detached** from the native view hierarchy, even though their React components are still mounted. So the window decides what exists; `removeClippedSubviews` decides which of the existing native views take part in drawing. ## What the prop buys The React Native docs describe the benefit as reduced **main-thread** work: detached views are excluded from the native rendering and drawing traversals, which lowers the risk of dropped frames while scrolling a long list with many mounted rows. On a ticker list whose window holds a couple of hundred mounted rows, only a screenful is visible at any moment; with the prop on, the other native views sit detached, so each drawing pass touches fewer views. The React side is unchanged: every mounted row still re-renders when data changes, because React components stay mounted whether or not their native views are attached. What it does **not** buy is much memory. The views are only detached, not deallocated, so they still exist and can be re-attached instantly when they scroll back into view. ## Defaults per platform | Platform | `FlatList` default in 0.87 | |---|---| | Android | `true` | | iOS | `false` | The default is computed in `FlatList` itself. The 0.87 source also carries a feature flag that would make it `true` on iOS as well, but that flag is off by default, so the practical default is still Android-only. You can set the prop explicitly on either platform. ## Why it can cause missing content Both the docs and the prop's own source comment warn that it may have bugs, with **missing content** as the symptom, observed mainly on iOS. The docs name the typical triggers: 1. **Transforms** on rows or their children, which move content visually without changing its layout box. 2. **Absolute positioning** that places content outside its parent's bounds. 3. Any layout where a child is drawn somewhere other than where its frame says it is. The native side decides visibility from view frames. If a view's frame says it is off screen but a transform or absolute offset draws it on screen, the view can be detached while it should be visible, and the user sees an empty row or a missing badge. In a ticker, a row that animates a price flash with a translate transform, or an absolutely positioned "up/down" badge, is exactly this shape. ## How to use it in practice - **Leave the Android default alone** unless you see missing content; it is on by default for a reason. - **Turn it on for iOS deliberately**, only on a list whose rows use plain layout, and test fast scrolls, rotations and rows with animations. - **When content goes missing**, try the list with the prop set to `false` first: if the bug disappears, the cause is a row whose drawn position disagrees with its frame. - **Do not use it as a memory fix**. To lower memory, shrink the window with `windowSize` or make rows lighter. ## Recent history worth knowing The changelog from 0.80 onward lists several fixes around this prop: keyboard navigation in lists that enable it and keeping a focused child from being clipped (both in 0.80), and a crash when its value changes at runtime (in later releases). They show the prop is maintained, and that its edge cases are real enough to need fixing.

  • An iOS FlatList shows an empty row only while a price-flash animation runs; how would you check whether removeClippedSubviews is the cause?
    Set the prop to `false` and repeat the scroll. If the row stops disappearing, a transform is drawing content outside the view's frame, so the native side detaches a view that is actually visible. Keep the prop off for that list, or change the row so its drawn position matches its layout.
  • Would enabling removeClippedSubviews reduce the memory use of a 10,000-row FlatList?
    Not significantly. It detaches native views but does not deallocate them, and it only affects rows that are already mounted inside the window. To reduce memory, shrink `windowSize` or make each row lighter.

saying these in an interview costs you the question

  • removeClippedSubviews unmounts React components outside the viewport
  • removeClippedSubviews is the main way to cut a list's memory use
  • FlatList enables removeClippedSubviews by default on both platforms
  • removeClippedSubviews and windowSize are two names for one mechanism
  • Turning removeClippedSubviews on is always safe on iOS