In React Native, how do the Text props numberOfLines and ellipsizeMode truncate text, and what limit does Android put on ellipsizeMode?
answer
- truncate after line wrapping
- numberOfLines default 0 means unlimited
- head, middle, tail, clip
- ellipsizeMode defaults to tail
- Android multi-line: only tail works
basics
~20 snumberOfLines caps a React Native Text at N laid-out lines (0, the default, means no limit), and ellipsizeMode picks where the cut shows: head, middle, tail (default) or clip. On Android, beyond one line only tail works correctly.
solid answer
~40 s`numberOfLines` truncates a `Text` so that, after the text has been laid out and wrapped, it shows at most that many lines; its default `0` means no limit, and setting it back to `0` removes the cap. `ellipsizeMode` only matters when `numberOfLines` is set and chooses where the ellipsis goes: `'tail'` (the default, `abcd…`), `'head'` (`…wxyz`), `'middle'` (`ab…yz`) or `'clip'`, which cuts at the edge without an ellipsis. On Android, when `numberOfLines` is greater than `1`, only `'tail'` works correctly, so `'head'` and `'middle'` are single-line tools in a cross-platform app. Truncation also needs a bounded width: a `Text` inside a `flexDirection: 'row'` container has `flexShrink: 0` by default, so it can overflow instead of truncating until you give it `flexShrink: 1` or `flex: 1`.
code
tsx · 24 linesimport {Image, StyleSheet, Text, View} from 'react-native';
type Props = {title: string; opening: string; avatarUri: string};
export function PoemRow({title, opening, avatarUri}: Props) {
return (
<View>
<View style={styles.row}>
<Image source={{uri: avatarUri}} style={styles.avatar} />
<Text style={styles.title} numberOfLines={1}>
{title}
</Text>
</View>
<Text numberOfLines={2}>{opening}</Text>
</View>
);
}
const styles = StyleSheet.create({
row: {flexDirection: 'row', alignItems: 'center'},
avatar: {width: 32, height: 32, marginRight: 8},
// Without flexShrink the title keeps its natural width and overflows the row.
title: {flexShrink: 1, fontWeight: 'bold'},
});go deeper
Recall the two props, the default of 0 meaning no limit, the four ellipsizeMode values with tail as default, and that ellipsizeMode needs numberOfLines.
Explain that truncation happens after wrapping, the Android rule that only tail works beyond one line, and why a Text in a row needs flexShrink to truncate at all.
Show how you design previews that truncate identically on both platforms, keep list rows even, and still give users a path to the full text.
Weigh visual consistency from hard line caps against content loss and localisation, where translated strings can double in length and change what gets cut.
## What the two props do `Text` lays out its content first, wrapping words onto as many lines as its width requires. **`numberOfLines`** then cuts that result so the visible text does not exceed the given number of lines, and marks the cut with an ellipsis. - **Default `0`**: no limit. Setting the prop to `0` later is how you remove a cap, for example when a user expands a preview. - **Any positive number**: at most that many lines are shown; the rest is hidden, not scrolled. - The cap applies to the **laid-out lines**, so the same string can need one line on a tablet and three on a phone. **`ellipsizeMode`** only has an effect together with `numberOfLines`. It decides which part of the text is sacrificed: | Value | Shown as | Typical use | |---|---|---| | `'tail'` (default) | `The fog comes on li…` | titles, previews, body text | | `'head'` | `…ttle cat feet` | a path or breadcrumb where the end matters | | `'middle'` | `Chapter…final.txt` | file names or identifiers where both ends matter | | `'clip'` | text cut at the edge, no ellipsis | decorative or fixed-width text | ## The Android limitation On Android, **when `numberOfLines` is greater than `1`, only `'tail'` works correctly**. `'head'` and `'middle'` are therefore reliable only for single-line text across both platforms. A multi-line preview that should truncate at the start or in the middle needs a different design, or platform-specific handling. ## Why truncation sometimes does not happen A cap on lines only helps if the `Text` knows how wide it may be. React Native's flexbox (Yoga) differs from the web in one default that matters here: 1. `flexShrink` defaults to **`0`** in React Native, not `1`. 2. In a `flexDirection: 'row'` container, a long `Text` next to an icon therefore keeps its natural width and **overflows** the row instead of shrinking. 3. Because its width is never constrained, the text does not wrap, so `numberOfLines={1}` has nothing to cut and the title runs off the screen or under the neighbouring icon. 4. Giving the `Text` `flexShrink: 1` (or `flex: 1`) lets it shrink to the space left, and truncation starts working. In a column layout the `Text` is usually stretched to the container's width, so the problem mostly appears in rows. ## A poetry list example A catalogue of poems typically shows each poem's title and its opening lines: - the **title** gets `numberOfLines={1}` with the default `'tail'`, inside a row with an author avatar, and `flexShrink: 1` so it truncates next to the avatar; - the **first lines** get `numberOfLines={2}` so every row has the same height, which keeps the list visually even; - a long **file name** for an attached recording can use `numberOfLines={1}` with `ellipsizeMode="middle"`, since both the start and the extension matter. ## Debugging a cap that looks wrong When truncation does not match expectations, check these in order: 1. **Is `numberOfLines` on the right `Text`?** On a nested `Text` inside a paragraph it does not cap the paragraph; put it on the outer `Text`, which owns the lines. 2. **Is the width bounded?** In a row, look for a missing `flexShrink: 1`; in an absolutely positioned container, look for missing `left`/`right` or `width`. 3. **Is the mode supported?** A three-line `'middle'` preview that looks right on iOS and wrong on Android is the documented Android limitation, not a bug in your layout. 4. **Is the string itself the problem?** Hard newlines in the data count as line breaks during layout, so a stanza with a blank second line spends one of its three lines on nothing. ## Things these props do not do - They do not tell you **whether** the text was truncated; that needs `onTextLayout` and a measurement of the full text. - They do not shrink the font to fit; font scaling to fit is a separate set of props. - They do not add a "more" affordance; any expand control is your own component. - Truncating hides content visually, so the full text should still be reachable, for example on a detail screen.
- In a React Native row, why does a Text with numberOfLines={1} run past the screen edge instead of showing an ellipsis?In React Native `flexShrink` defaults to `0`, so a `Text` inside a `flexDirection: 'row'` container keeps its natural width and overflows rather than shrinking. Its width is never constrained, so there is nothing to truncate. Adding `flexShrink: 1` or `flex: 1` to the `Text` lets it shrink to the remaining space, and `numberOfLines` then cuts it with an ellipsis.
- In React Native, how do you remove a numberOfLines cap when a user taps to expand a preview?Render the same `Text` with `numberOfLines` set to `0`, which the prop treats as no limit, for example `numberOfLines={expanded ? 0 : 3}`. The text is laid out again at full length. Knowing whether to show the expand control at all is a separate problem that needs the text's measured line count.
saying these in an interview costs you the question
- numberOfLines counts newline characters in the string rather than laid-out lines.
- ellipsizeMode 'middle' works the same on Android for a three-line preview as on iOS.
- ellipsizeMode alone truncates text even when numberOfLines is not set.
- numberOfLines={0} hides the text entirely.
- A Text in a row truncates automatically because flexShrink defaults to 1, as on the web.