In React Native, why does a long Text beside an icon in a flexDirection row run past the screen edge instead of wrapping?
answer
- the row cannot take space back
- flexShrink defaults to 0
- Text is sized by its measured content
- shrink the Text, not the icon
- flexShrink: 1 or flex: 1 on Text
basics
~20 sYoga's default flexShrink is 0, so the Text keeps the width it measured for its content even when the row is too narrow, and overflows. Setting flexShrink: 1 (or flex: 1) on the Text lets it narrow and wrap.
solid answer
~40 sIn React Native every flex item starts with `flexShrink: 0`. A `Text` in a row is sized from its measured content, so a long feature line asks for more width than the row has, and because it may not shrink, Yoga leaves it at that width and the line runs off the edge. The fix goes on the item that should give way: `flexShrink: 1` on the `Text` lets it narrow to the space left after the icon, and a narrower `Text` wraps onto more lines. `flex: 1` also works but additionally makes the `Text` grow into any spare space. Leave the icon at `flexShrink: 0` so it keeps its size, and remember that `numberOfLines` only truncates once the `Text` has been given that narrower width.
code
tsx · 18 linesimport { Image, StyleSheet, Text, View } from 'react-native';
type Props = { iconUri: string; label: string };
export function FeatureLine({ iconUri, label }: Props) {
return (
<View style={styles.row}>
<Image source={{ uri: iconUri }} style={styles.icon} />
<Text style={styles.label}>{label}</Text>
</View>
);
}
const styles = StyleSheet.create({
row: { flexDirection: 'row', alignItems: 'center', gap: 8 },
icon: { width: 20, height: 20 },
label: { flexShrink: 1 },
});go deeper
Recall that React Native items do not shrink by default, and that the fix is flexShrink: 1 or flex: 1 on the Text, not on the icon.
Explain the steps: the Text's basis is its measured one-line width, the row has negative free space, and only items with flexShrink above 0 give it back.
Show how you would audit rows across an app: which child is flexible, nested wrappers that also need to shrink, long translated strings and narrow devices.
Frame it as a component-library default: whether shared row components should build in a flexible label slot so product teams never hit this overflow.
## The symptom A pricing card lists features as rows: a small check icon, then a line of text. Short lines look fine. A long line such as "Unlimited projects with priority support and shared team workspaces" does not wrap; it runs past the card and off the screen. Nothing is clipped or warned about, and the same markup ported from a web page wrapped perfectly there. ## What Yoga does with the row **Yoga**, React Native's layout engine, lays out the row in a few steps: 1. The container has `flexDirection: 'row'`, so the main axis is horizontal. 2. Each child gets a **flex basis**. The icon's is its `width: 20`. The `Text` has no width, so its basis comes from **measuring its content**: laid out on one line, the sentence is wider than the row. 3. The children's bases plus any `gap` exceed the row's width, so there is **negative free space** to remove. 4. Yoga removes negative space only from items whose `flexShrink` is above zero. In React Native the default `flexShrink` is **0** for every item, so nobody shrinks. 5. The `Text` keeps its one-line width, its right edge lies past the container, and the line is drawn past the edge. The same scenario on the web wraps because there `flex-shrink` defaults to 1. In React Native you opt **into** shrinking on the item that should give way. ## Fixes compared | Style on the `Text` | Shrinks when too long | Grows into spare space | Typical use | |---|---|---|---| | `flexShrink: 1` | Yes | No | Label that should wrap but stay content-sized | | `flex: 1` | Yes, in practice, from a zero basis | Yes | Label that should take all remaining width, e.g. to push a price to the far end | | `width: 240` | No, fixed | No | Rarely right; breaks on narrow phones and large text | | `numberOfLines={1}` alone | No | No | Does nothing useful until the `Text` is narrower than its content | `flex: 1` works because a positive `flex` sets a basis of 0 and grows from there, so the `Text` never asks for more than the leftover width. The trade-off is that it always fills the row, which matters if something should sit right after the text. ## Keeping the icon intact Once the `Text` can shrink, the icon is safe because it still has `flexShrink: 0`. The mistake to avoid is the opposite fix, putting `flexShrink: 1` on the **container's children in general**, or on the icon: if both can shrink, Yoga takes space from each in proportion to its shrink factor and its basis, and the icon can be squeezed. Keep fixed-size items such as icons, avatars and prices at the default, and give shrinking only to the flexible label. ## Truncation instead of wrapping Sometimes the design wants one line with an ellipsis. `numberOfLines={1}` on the `Text` truncates the text after layout, and `ellipsizeMode` already defaults to `'tail'`. But truncation happens **within the width the Text was given**. With `flexShrink: 0` that width is the full one-line content width, so there is nothing to truncate and the line still overflows. Combine `numberOfLines` with `flexShrink: 1` so the `Text` is first narrowed and then ellipsized. ## Confirming the cause quickly Before changing styles, make the boxes visible: - Give the `Text` a temporary `backgroundColor`. If the coloured box itself runs past the card, the `Text` was laid out too wide; that is the shrink problem, not a clipping or font issue. - Give the row a temporary `borderWidth`. If the row is the right width and only its child is too wide, the fix belongs on the child. - Add `flexShrink: 1` to the `Text` and watch the box narrow and the text wrap onto a second line. If instead the row itself is wider than the card, the problem sits one level up: some ancestor is too wide or cannot shrink, and the same diagnosis repeats there. ## A review checklist - In any `flexDirection: 'row'`, decide which child is flexible and give only that child `flexShrink: 1` or `flex: 1`. - Check nested rows: a `Text` inside a `View` inside a row needs the **wrapping `View`** to shrink too, or the inner `Text` has no narrower width to wrap into. - Test with the longest real strings, including translations, rather than placeholder copy. - Test a narrow device, because a row that fits on a large phone can overflow on a small one. The underlying rule is short: in React Native nothing shrinks unless you say so, and a `Text` measures itself at its natural one-line width.
- The Text sits inside a wrapper View that is the row's child, and flexShrink: 1 on the Text changed nothing. Why?The row shrinks its direct children, and the direct child is the wrapper `View`, which still has `flexShrink: 0` and keeps its content width. The `Text` inside is laid out in that wide wrapper, so it has no reason to wrap. Put `flexShrink: 1` (or `flex: 1`) on the wrapper as well, so the narrower width reaches the `Text`.
- When would you choose flex: 1 over flexShrink: 1 for the label?When the label should also take every spare point of width, for example to push a price or a chevron to the row's far end. `flex: 1` grows from a zero basis, so it both fills and never overflows. `flexShrink: 1` keeps the label content-sized when it fits, which is better when something must sit right after it.
saying these in an interview costs you the question
- Text in React Native wraps automatically inside a row, as it does on the web.
- flexWrap: 'wrap' on the row makes the long Text break onto several lines.
- Putting flexShrink: 1 on every child is the safe fix.
- numberOfLines={1} alone stops the Text from overflowing the row.
- The overflow happens because ellipsizeMode is off by default.