skip to content

View & Text Rules

View is the container for everything, and raw strings must sit inside Text, the one component whose styles nested children inherit. Interviewers use it to catch web habits in React Native.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

6

In React Native, why must raw strings sit inside a Text component instead of a View, and what happens when one does not?

level: juniorimportance: must knowfreq 78%

answer

  1. View is a box, not a text surface
  2. text layout lives only inside Text
  3. a string child needs a Text ancestor
  4. count && <Text> can leak a 0
  5. 'Text strings must be rendered' error

basics

~20 s

A React Native View is a layout box with no text drawing; only Text owns text layout. A string or number placed directly in a View is not drawn, and development builds report 'Text strings must be rendered within a <Text> component.'

solid answer

~40 s

`View` maps to a plain native view (a `UIView` on iOS, an `android.view.View` subclass on Android) laid out by flexbox; it has no idea how to draw characters. `Text` maps to a native paragraph view that builds an attributed string from its children. When the renderer creates a text node it checks whether it has a `Text` (or `TextInput`) ancestor. If not, the node has no text layout to live in, so nothing is drawn, and a development build logs `Text strings must be rendered within a <Text> component.` Older renderers threw it, which is why many people remember it as a crash. The usual culprits are `{count && <Text/>}` with `count === 0`, a stray `{' '}` between elements, and a string variable passed as a `View` child.

code

tsx · 24 lines
tsx
import {Text, View} from 'react-native';

type Props = {likes: number; title: string};

// Broken: likes === 0 renders a bare 0 inside the View,
// and title is a string child of a View.
export function BrokenHeader({likes, title}: Props) {
  return (
    <View>
      {title}
      {likes && <Text>{likes} likes</Text>}
    </View>
  );
}

// Fixed: every string lives inside Text; the conditional yields an element or null.
export function Header({likes, title}: Props) {
  return (
    <View>
      <Text>{title}</Text>
      {likes > 0 ? <Text>{likes} likes</Text> : null}
    </View>
  );
}

go deeper

for a junior

Recall the rule and the exact error message, and be ready to spot the two classic causes: a numeric short-circuit like count && and a string variable dropped straight into a View.

for a middle

Explain why the rule exists: a View is a flexbox box with no text engine, while Text builds an attributed string. Mention that numbers count and fragments do not help.

for a senior

Show you treat the error as a shipped bug, since release builds silently lose the text, and describe the conventions that prevent it: boolean conditionals and containers that wrap string children in Text.

for a principal

Frame the rule as a deliberate trade: no inherited text styling from containers keeps components isolated, at the cost of a shared text component every team must agree on and enforce.

## Two kinds of native view React Native does not render to a document. Every host component becomes a real native view, and the two most basic ones do very different jobs: - **`View`** is a container. It maps to the platform's plain view (`UIView` on iOS, an `android.view.View` subclass on Android), takes part in **flexbox layout computed by Yoga**, and can carry style, touch handling and accessibility information. It has no text engine. - **`Text`** is the only core component that draws characters. It maps to a native **paragraph** view. Everything inside it is laid out with **text layout** (line breaking, wrapping, spans), not flexbox. So "put the string in a `View`" asks a box that cannot draw glyphs to draw glyphs. React Native refuses rather than guessing a font, size and colour for you. ## What the renderer actually checks When JSX has a string or number child, React asks the renderer to create a **text instance**. React Native's renderer tracks, as it walks down the tree, whether the current position is inside a text parent. Only the text host components (`Text`, its nested virtual-text form, and `TextInput`) switch that flag on. 1. A string or number child becomes a raw-text node. 2. The renderer asks: is there a `Text` or `TextInput` ancestor? 3. **Yes**: the raw text is folded into that paragraph's attributed string and drawn. 4. **No**: in a development build the renderer logs `Text strings must be rendered within a <Text> component.`, which LogBox surfaces. The raw-text node has no layout of its own and does not become a native view, so **nothing appears on screen**, in development or in release. In older React Native releases the same check threw, so the app hit a red error screen. In 0.87 the renderer reports it with `console.error` rather than throwing, but it is still a bug: the text the user was meant to see is missing. React Native Testing Library 14 always validates this and throws in tests, so a covered screen fails there first. ## Where stray strings come from | Source | Example | Fix | |---|---|---| | A number short-circuit | `{likes && <Text>{likes} likes</Text>}` with `likes === 0` renders `0` | `{likes > 0 ? <Text>…</Text> : null}` | | A same-line space between tags | `<Text>a</Text> <Text>b</Text>` inside a `View` | move the space inside a `Text`, or drop it | | A string variable as a child | `<View>{title}</View>` | `<View><Text>{title}</Text></View>` | | A typo in JSX | `<Text>Hi</Text>;` inside a parent `View` renders `;` | delete the stray character | | A wrapper that renders children in a `View` | `<Card>Hello</Card>` where `Card` returns a `View` | make `Card` wrap string children in `Text`, or pass a `Text` | Two details trip people up: - **Numbers count too.** `<View>{42}</View>` is the same error, because a number child is rendered as text. - **Indentation is safe.** JSX removes whitespace that contains a line break, so tags on separate lines inside a `View` do not create text nodes. A space on the same line as two tags is kept, and becomes a stray string. - **Fragments do not help.** `<>{title}</>` is not a host component, so the string still has no `Text` ancestor. - **Empty strings are ignored.** React does not create a text node for `''`, so a conditional that yields an empty string renders nothing; a conditional that yields `0` renders the digit. ## Why the rule exists Making `Text` the only place characters can live keeps text styling explicit. A `View` has no font, colour or line-height to hand down, so a component always looks the same wherever it is dropped, and the native side only needs text attributes on the paragraph that uses them. The cost is that you cannot set a default font on a screen's root container; the usual answer is a small shared text component that wraps `Text` with the app's defaults. ## Habits that prevent it - Render conditionals with a boolean or a ternary: `{items.length > 0 ? <List /> : null}`. - Keep inline spaces inside the `Text` they belong to: `<Text>Hello, <Text style={styles.name}>{name}</Text></Text>`. - Give reusable containers a clear contract: either they accept only elements, or they wrap string children in `Text` themselves. - Treat the LogBox error as a failed build in review; the release build will silently show a gap where the text should be.

  • Why does `{items.length && <Text>…</Text>}` inside a React Native View break only when the list is empty?
    `&&` returns its left operand when that operand is falsy. For an empty list that operand is the number `0`, and React renders numbers as text. The `0` lands directly inside the `View`, with no `Text` ancestor, so it triggers the raw-text error and is not drawn. With one or more items the expression returns the `Text` element and everything works. Use `items.length > 0 ? … : null` instead.
  • Inside a React Native View, is a line break between two Text elements a problem, and is a space on the same line?
    A line break is fine: JSX drops whitespace that contains a newline, so indenting children on separate lines creates no text node. A space between two tags on the same line is kept as a string child, so inside a `View` it becomes a stray raw-text node and triggers the error. Put the space inside one of the `Text` elements instead.
  • Does wrapping a bare string in a React fragment inside a React Native View avoid the raw-text error?
    No. A fragment is not a host component; it disappears during rendering and its children attach to the nearest host parent, here the `View`. The renderer still finds no `Text` ancestor for the string, so the error and the missing text remain. Only a `Text` (or a component that renders one) around the string fixes it.

saying these in an interview costs you the question

  • A View renders a string child like any other child, just with default styling.
  • The error only matters in development; release builds show the text normally.
  • Wrapping the stray string in a fragment is enough to fix the error.
  • Numbers are safe outside Text because the check only looks at strings.
  • Indenting Text children on separate lines inside a View creates stray whitespace nodes.
open as a page

In a React Native poetry app, how do you render one paragraph with inline bold and tappable linked words, and what do nested pieces inherit?

level: middleimportance: must knowfreq 62%

basics

~20 s

Nest Text inside Text: React Native flattens the tree into one attributed string, so bold or linked words are inline spans that wrap with the line. Nested Text inherits its ancestor Text's text styles and can override them; a View passes none down.

open as a page

In React Native, how do the Text props numberOfLines and ellipsizeMode truncate text, and what limit does Android put on ellipsizeMode?

level: juniorimportance: should knowfreq 46%

basics

~20 s

numberOfLines 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.

open as a page

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%

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.

open as a page

In React Native, what do the four View pointerEvents values do, and which lets taps pass through an overlay while its buttons work?

level: middleimportance: should knowfreq 30%

basics

~20 s

pointerEvents sets whether a React Native View and its children can be touch targets: 'auto' (both), 'none' (neither), 'box-none' (children only) and 'box-only' (the view only). An overlay uses 'box-none' so empty areas pass taps through.

open as a page

In a React Native poem reader, how would you show a 'Read more' toggle only when a stanza really exceeds three lines, using onTextLayout?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Measure the stanza's full line count with onTextLayout on a hidden, uncapped copy of the Text at the same width and style. If nativeEvent.lines.length exceeds three, show the toggle and cap the visible Text with numberOfLines={expanded ? 0 : 3}.

open as a page