In a React Native FlatList, what does getItemLayout do, when can you supply it, and what goes wrong if its offsets are wrong?
answer
- length, offset and index
- skips measuring rendered cells
- separators count toward the offset
- never corrected by measurement
basics
~20 sgetItemLayout returns each row's length and offset from the data, so FlatList can skip measuring rendered cells and size spacers exactly. It suits rows whose sizes are known in advance; wrong offsets cause jumping content and misplaced scrollToIndex targets.
solid answer
~40 s`getItemLayout(data, index)` returns `{length, offset, index}`: the row's size along the scroll axis and its distance from the start of the content. With it, `FlatList` stops attaching layout listeners to each cell, sizes the spacers for unrendered rows exactly, knows the full content length at once, and can `scrollToIndex` to rows it never rendered. You can use it when sizes come from the data alone: fixed-height rows, plus any fixed separator or header added into the offset. It does not set a row's height; rows still lay out from their styles. If the function disagrees with reality, content jumps as spacers are replaced, scroll targets land on the wrong row, and the list never corrects itself because it is no longer measuring.
code
tsx · 28 linesimport { FlatList, Text, View } from 'react-native';
const ROW = 72;
const SEPARATOR = 1;
const HEADER = 48;
type Quote = { symbol: string; priceText: string };
export function Watchlist({ quotes }: { quotes: Quote[] }) {
return (
<FlatList
data={quotes}
keyExtractor={(q) => q.symbol}
ListHeaderComponent={<View style={{ height: HEADER }} />}
ItemSeparatorComponent={() => <View style={{ height: SEPARATOR }} />}
renderItem={({ item }) => (
<View style={{ height: ROW }}>
<Text>{item.symbol} {item.priceText}</Text>
</View>
)}
getItemLayout={(_, index) => ({
length: ROW,
offset: HEADER + (ROW + SEPARATOR) * index,
index,
})}
/>
);
}go deeper
Recall that getItemLayout returns length, offset and index for a row, and that it only works when every row's size is known in advance.
Explain what the list skips with it, why separators and headers belong in the offset, and how it enables scrollToIndex to rows that were never rendered.
Diagnose jumping content or wrong scroll targets as a getItemLayout mismatch, and keep the function tied to the row's style constants with a test.
Judge when a design should commit to fixed row sizes for list performance, and when variable content is worth the cost of measured layout.
## What getItemLayout is `getItemLayout` is an optional `FlatList` prop (inherited from `VirtualizedList`) with the signature `(data, index) => {length, offset, index}`: - **`length`** is the row's size along the scroll axis: its height in a vertical list, its width in a horizontal one. - **`offset`** is the distance from the start of the scroll content to the start of that row. - **`index`** echoes the row index. It tells the list, ahead of time, where every row is. The list calls it for many indices while scrolling, so it must be cheap: arithmetic or an array lookup, never a search through `data`. Without it, the list only knows a row's size after the row has rendered and reported its layout, and it estimates everything else from an **average of the rows it has measured**. ## What the list does differently when you supply it 1. **No per-cell layout listeners.** The list normally attaches a layout callback to every mounted cell to record its size. With `getItemLayout`, it skips that (unless debugging aids such as the `debug` prop are on), saving work on every mount. 2. **Exact spacers.** Unrendered regions are drawn as spacer `View`s. With `getItemLayout`, their sizes are computed exactly, so the content does not jump when real rows replace spacers. 3. **The full length is known at once.** Without `getItemLayout`, the trailing spacer is limited to the highest row measured so far, so a fling cannot travel into unmeasured territory and the scroll indicator shifts as more rows are measured. With it, the whole list's length is known from the start. 4. **Earlier high-priority rendering.** The list only starts urgent, immediate rendering passes once it has size information; `getItemLayout` provides it before any row has been measured. 5. **Scrolling to any index.** `scrollToIndex` to a row that was never rendered needs its offset; with `getItemLayout` the list can compute it instead of failing. ## When you can use it The requirement is that **offsets can be computed from the data alone**, before rendering: - **Fixed-height rows**: `offset = ROW * index`, the common case. - **Rows plus a fixed separator**: `ItemSeparatorComponent` adds length between rows, so the offset must include it: `offset = (ROW + SEPARATOR) * index`. The docs call this out explicitly. - **A fixed-height list header**: offsets are measured from the start of the scroll content, so a header's height is added to every offset. - **A few known row types**: precompute a prefix-sum array of offsets when `data` changes, and look it up by index. If row height depends on text wrapping, dynamic type or images of unknown size, you cannot know it in advance, and a guess is worse than no `getItemLayout` at all. ## What goes wrong when it is wrong `getItemLayout` does **not** set a row's size. Rows still lay out with their own styles; the function only tells the list what to expect. If the two disagree: | Mistake | Symptom | |---|---| | Returned height smaller than the real one | Rendered rows drift from where the list expects them, so content jumps as spacers are swapped for real rows and gaps can open | | Separator left out of `offset` | Error accumulates with index: row 500 is 500 separators away from where the list thinks it is | | Header height left out | Every `scrollToIndex` lands a header's height off target | | Guessed heights for variable rows | Wrong window calculations, rows popping in and out, wrong viewability callbacks | Because the list stops attaching layout listeners when `getItemLayout` is present, it never corrects a wrong value by measuring the rendered rows. The error persists for as long as the function is wrong. ## How to write it safely - Derive the numbers from **the same constants** the row's style uses, so a design change cannot update one and not the other. - Test it: render the list and compare real row positions against the function for a few indices, including a high one. - Leave it out when rows are genuinely variable; the measured path is slower but correct.
- How would you support two known row heights, such as quote rows and section-label rows?Precompute an array of cumulative offsets whenever `data` changes, adding each row's known height plus the separator, and have `getItemLayout` look up `offsets[index]` and the matching length. It stays correct as long as each row's type, and therefore its height, is known from the data.
- Why can getItemLayout let a fling reach blank space that a measured list would not?Without it, the trailing spacer is limited to the highest measured row, so the content ends near what has rendered. With it, the whole length is known, so a fling can travel deep into unrendered rows and briefly show spacers there, while positions stay stable and no content jumps.
saying these in an interview costs you the question
- getItemLayout sets the height each row renders at
- offset is the row's position within the visible screen
- Separators are measured automatically, so offsets can ignore them
- The list re-measures rows to correct a wrong getItemLayout
- getItemLayout only matters for horizontal lists