skip to content

In React Native, what does a View's onLayout callback report, when does it fire, and what goes wrong when you copy its result into state?

level: middleimportance: should knowfreq 48%

answer

  1. an event, not a synchronous read
  2. nativeEvent.layout: x, y, width, height
  3. x and y are relative to the parent
  4. fires on mount and on layout change
  5. state from it means an extra render

basics

~20 s

onLayout delivers nativeEvent.layout (x, y, width, height, with x and y relative to the parent) on mount and whenever the layout changes. It arrives after layout, so copying it into state costs a second render and a visible jump.

solid answer

~50 s

`onLayout` is available on `View` and on components built on it, such as `Text`, `Image`, `Pressable`, `ScrollView` and `TextInput`. It is called with an event whose `nativeEvent.layout` holds `x`, `y`, `width` and `height`; `x` and `y` are relative to the parent. It fires on mount and again whenever the computed layout changes, and the docs warn the new layout may not be on screen yet, especially during a layout animation. The trap is `setState` inside it: the first frame renders without the measurement, then a second render applies it, so anything positioned from the value jumps into place. If the new state changes the measured view's own size, you can get a feedback loop. Use it for values you can apply a frame late, derive layout with flexbox where you can, and keep synchronous pre-paint measurement for the APIs designed for it.

code

tsx · 26 lines
tsx
import {useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import type {LayoutChangeEvent} from 'react-native';

export function StanzaCard({body, note}: {body: string; note: string}) {
  const [height, setHeight] = useState<number | null>(null);

  const onLayout = (e: LayoutChangeEvent) => {
    const next = e.nativeEvent.layout.height;
    setHeight(prev => (prev === next ? prev : next));
  };

  return (
    <View style={styles.card} onLayout={onLayout}>
      <Text>{body}</Text>
      {height !== null ? (
        <Text style={[styles.note, {top: height / 2}]}>{note}</Text>
      ) : null}
    </View>
  );
}

const styles = StyleSheet.create({
  card: {padding: 16},
  note: {position: 'absolute', right: 8, fontStyle: 'italic'},
});

go deeper

for a junior

Recall the shape of the event, nativeEvent.layout with x, y, width and height, and that it fires on mount and on layout changes.

for a middle

Explain that x and y are parent-relative, why the event arrives after layout, and why putting the value in state causes a second render and a visible jump.

for a senior

Show how you avoid measurement where flexbox suffices, guard updates, prevent size feedback loops and choose a synchronous read only when a frame of delay is unacceptable.

for a principal

Set team guidance on when layout measurement belongs in JavaScript at all, trading flexibility against extra renders and jank on low-end devices.

## What onLayout is React Native computes layout with **Yoga**, its flexbox engine, and then applies frames to native views. `onLayout` is how JavaScript is told the result for one component. It is a prop on `View` and on the core components built on it (`Text`, `Image`, `Pressable`, `ScrollView`, `TextInput` and others). The callback receives a layout event. The useful part is `nativeEvent.layout`: | Field | Meaning | |---|---| | `x` | horizontal offset from the **parent**'s origin | | `y` | vertical offset from the **parent**'s origin | | `width` | the component's computed width | | `height` | the component's computed height | The TypeScript type for the event is `LayoutChangeEvent`, exported from `react-native`. ## When it fires - **On mount**, once the component's layout has first been computed. - **On layout changes**, whenever the computed frame differs from the previous one: new content, a parent resize, a rotation, a style change. - It does **not** fire on every render; a render that leaves the frame unchanged produces no event. The documentation adds an important caveat: the event is fired as soon as the layout has been calculated, but **the new layout may not yet be reflected on screen** when you receive it, especially while a layout animation is running. Treat it as a report about layout, not a guarantee about pixels. ## The state trap The most common use is also the most common bug: measuring something and storing the numbers in state. 1. The component renders without the measurement, so anything that depends on it uses a default (often `0`). 2. Layout runs and `onLayout` delivers the real size. 3. `setState` schedules a second render with the real value. 4. The second render commits, and whatever was positioned from the value **moves**. Users see a one-frame jump: an annotation marker appearing at the top of a stanza card and snapping down, or a tooltip flashing in the corner. Worse, if the new state changes the **size of the measured view itself**, for example by adding content inside it, `onLayout` fires again with a new size, the state changes again, and the component can oscillate or loop. ## Using it well - **Prefer layout to measurement.** Flexbox, `position: 'absolute'` with offsets, and percentage sizes solve most "place this relative to that" problems without reading any numbers in JavaScript. - **Measure the right node.** `onLayout` reports a component's own frame relative to its parent, not relative to the screen. Converting to screen coordinates is a different API. - **Guard state updates.** Compare with the stored value and skip the update when width and height have not changed, so re-layouts that do not matter do not cause renders. - **Never let the measured view's size depend on its own measurement** unless the dependency is designed to converge. - **Use it for values that can arrive a frame late**, such as sizing a chart to the space it was given, recording a section's offset for later scrolling, or choosing a layout for a wide container. - **For pre-paint precision** use the New Architecture's synchronous layout reads, which are a separate topic. ## onLayout versus other ways to know a size | Need | Tool | |---|---| | A child should fill or share its parent's space | flexbox (`flex`, `flexGrow`, `alignSelf`), no measurement | | A component should adapt to the space it was given | `onLayout` on the container, applied a frame later | | A value must be known before the first paint | a synchronous layout read in the New Architecture | | A view's position on the whole screen | a measurement API that converts to window coordinates | The table is the real interview answer behind "when would you use onLayout": when a layout-dependent value may arrive one frame late and flexbox cannot express the rule. ## A poetry-reader example A stanza card shows a margin note beside a specific line. The card's height depends on the stanza, so the note's vertical position is not known up front. Measuring the card with `onLayout` and storing its height works, but the note is first drawn at `y = 0` and then jumps. Either accept that for content below the fold, or hide the note until the measurement exists so it appears once, in the right place: - keep `height` in state, initially `null`; - render the note only when `height !== null`; - update `height` only when it actually changes.

  • In React Native, are the x and y from onLayout screen coordinates?
    No. `nativeEvent.layout.x` and `y` are offsets from the component's parent, as computed by the layout pass. Two views at the same screen position inside different parents can report different values. Knowing where a view sits on the screen, for instance to position a popover, needs a different measurement API that converts to window coordinates.
  • In React Native, why can storing onLayout's height in state make a component re-render repeatedly?
    If the stored height changes what the measured view renders, such as adding or removing content inside it, the view's size changes, `onLayout` fires again with a new height, and state updates again. Unless the values converge, the component keeps re-laying out. Keep the measured view's size independent of its own measurement and skip updates when the value is unchanged.

saying these in an interview costs you the question

  • onLayout reports the component's position on the screen, not relative to its parent.
  • onLayout fires after every render, whether or not the layout changed.
  • Values from onLayout are applied before the first frame is painted, so nothing ever jumps.
  • onLayout is only available on View, not on Text or Image.
  • Setting state from onLayout is free because it does not trigger another render.