Since React Native 0.77, what do boxSizing: 'content-box' and display: 'contents' change about how Yoga lays out a View?
answer
- two 0.77 layout props
- border-box is still the default
- content-box adds padding and border outside
- contents drops the wrapper's own box
- children lay out as the parent's
basics
~20 sboxSizing: 'content-box' makes width and height apply to the content box, so padding and border are added outside; the default remains border-box. display: 'contents' removes a View's own layout box so its children lay out as direct children of its parent.
solid answer
~40 sBoth props arrived in React Native 0.77. `boxSizing` decides what `width`, `height`, `minWidth` and the other size props measure. The default is `border-box`: a `width: 100` view with `padding: 10` is 100 points wide overall. With `content-box`, the 100 points is the content area and padding and border are added outside, making it 120. The default stayed `border-box` because changing it would have broken existing layouts. `display` accepts `'flex'` (the default), `'none'` and `'contents'`. With `'contents'`, the view generates no layout box and is flattened out of the view hierarchy; its children are laid out as if they were the parent's direct children, so a wrapper can observe bubbled pointer events or provide context without adding a box that disturbs the parent's flex layout.
code
tsx · 14 linesimport type { ReactNode } from 'react';
import { StyleSheet, View } from 'react-native';
export function PressTracker({ children, onPress }: { children: ReactNode; onPress: () => void }) {
return (
<View style={styles.passThrough} onPointerDown={onPress}>
{children}
</View>
);
}
const styles = StyleSheet.create({
passThrough: { display: 'contents' },
});go deeper
Recall that React Native sizes views as border-box by default and that display accepts flex, none and contents.
Explain how content-box changes the arithmetic of width, padding and border, and how display: 'contents' lets a wrapper's children join the parent's flex layout.
Use display: 'contents' to keep behaviour wrappers out of layout in shared components, and treat any boxSizing change on a shared component as a visual regression risk.
Decide whether a component library should adopt these props widely or keep them as targeted fixes, given every screen already assumes border-box sizing and one box per wrapper.
## Two newer layout props React Native 0.77 added two layout capabilities to **Yoga**, the engine that sizes and positions every view: a choice of **box sizing**, and a third **display** value, `'contents'`. Neither is needed for everyday screens, but both solve specific problems that were awkward before. ## boxSizing: what width measures Every view has a content area, padding around it, and a border around that. `boxSizing` decides which box the size props describe: | `boxSizing` | `width: 100`, `padding: 10`, `borderWidth: 1` | Size props describe | |---|---|---| | `border-box` (default) | 100 points wide overall; content area 78 | The border box: content, padding and border together | | `content-box` | 122 points wide overall; content area 100 | The content box only; padding and border are added outside | Points to remember: - `border-box` has always been React Native's behaviour, and it **stays the default** because switching would have broken existing layouts. - It applies to `width`, `height`, `minWidth`, `minHeight`, `maxWidth` and `maxHeight` alike. - `content-box` is useful when the **content** must have an exact size, for example a 44-point drawing area whose padding and border can vary by theme, or when matching a layout specified in content sizes. A worked example makes the difference concrete. A plan card's price badge must have a 40-point square content area for an icon, while its padding changes between a compact and a comfortable theme. With the default `border-box`, `width: 40` plus `padding: 8` leaves only 24 points of content, and the icon shrinks when padding grows. With `boxSizing: 'content-box'`, the content stays 40 points and the badge grows outward to 56 points instead. ## display: the three values React Native's `display` style accepts only three values: 1. `'flex'`, the default: the view is a flex container and takes part in its parent's layout. 2. `'none'`: the view and its children are removed from layout and take no space. 3. `'contents'`: the view itself generates **no layout box**, but its children keep theirs and are laid out as if they were direct children of the view's parent. ## What display: 'contents' is for A wrapper component often exists for reasons that have nothing to do with layout: it provides a context value, observes pointer events bubbling up from its children, or measures something. The extra `View` still normally creates a box, and that box can break the parent's flex layout. In a pricing row with `flexDirection: 'row'` and `gap`, wrapping two of the three cards in a tracking wrapper turns them into one flex item, so the gap and the equal split are lost. With `display: 'contents'` on the wrapper: - The cards become direct flex items of the row again, so `gap` and `flex: 1` apply to each. - The wrapper is flattened out of the view hierarchy. - Pointer events from the cards, such as `onPointerDown`, still bubble to the wrapper's handler, but only presses inside the cards' own bounds trigger it, since the wrapper has no area of its own. - Styles that need a box of their own, such as the wrapper's own padding, border or size, have nothing to apply to. ## Choosing between them and older habits | Need | Prop | |---|---| | Hide a view and free its space | `display: 'none'` | | Keep a wrapper for behaviour without a layout box | `display: 'contents'` | | Make size props describe the content area | `boxSizing: 'content-box'` | | Keep the usual behaviour | leave `boxSizing` at `'border-box'` | Before 0.77, the workaround for a behaviour-only wrapper was to pass styles down or restructure components so the wrapper did not sit between a flex container and its items. That still works, but `display: 'contents'` removes the need in many cases. ## Things to verify - Changing `boxSizing` on a shared component changes every screen that sets a size on it; treat it as a visual change. - A `display: 'contents'` wrapper has no box of its own to measure; when you need a size, measure one of its children instead. - Both props exist on React Native 0.77 and later, so on Expo SDK 57 (React Native 0.86) and on 0.87 they are always available.
- How is display: 'contents' different from display: 'none'?`'none'` removes the view and all of its children from layout, so nothing renders and no space is taken. `'contents'` removes only the view's own box: its children still render and are laid out as if they belonged directly to the view's parent.
- Why did React Native keep border-box as the default when adding content-box?Every existing React Native layout had been written against border-box sizing, the only behaviour before 0.77. Switching the default to the web's `content-box` would have silently resized views with padding or borders across every app, so `content-box` became an opt-in value instead.
saying these in an interview costs you the question
- boxSizing defaults to content-box in React Native, as on the web.
- With border-box, padding is added on top of the width.
- display: 'contents' hides the view and its children.
- React Native's display accepts block and inline-block values.
- A display: 'contents' wrapper stops its children's pointer events from bubbling.