React Native Boost

Optimization Coverage

What React Native Boost can optimize today, what it skips, and why.

React Native Boost is conservative by design. If it cannot prove an optimization is safe, it skips it. While this means it'll often skip optimizations that would be safe in practice, it also means that you can trust that optimizations that do happen are safe and won't cause behavioral changes or other bugs.

At a Glance

ComponentOptimized when...Common bailout reasons
TextImported from react-native, no blacklisted props, primitive children, safe ancestor chaincontains blacklisted props, has Text ancestor, has unresolved ancestor and dangerous optimization is disabled, contains non-primitive children, is a direct child of expo-router Link with asChild
ViewImported from react-native, safe ancestor chain, no spread that may carry a translated prophas a spread that may carry a translated prop, has both a dynamic id and a nativeID (ambiguous precedence), has Text ancestor, has unresolved ancestor and dangerous optimization is disabled
ImageOpted in with optimizations.image: true, imported from react-native, native platform known, supported source/style props, safe ancestor chaintarget platform is unknown, has a Unistyles style and there is no lean Image host to route to, has an unresolved style source that may be a Unistyles style, contains unsupported Image props, has a spread that may carry Image wrapper props, has Text ancestor, has unresolved ancestor and dangerous optimization is disabled

Global Bailouts

These skip optimization before component-specific checks:

  • File path matches ignores
  • Line is marked with @boost-ignore

No log for ignored files

Files skipped via ignores are filtered before optimizer checks, so you will not see per-component skip logs for those files.

Overriding Bailouts

Use @boost-force to force optimization on a component that would otherwise be skipped. This bypasses all bailout checks except the react-native import check. See the Decorators page for details.

Text Coverage

Text is optimized when all checks pass.

Text blacklisted props

If any of these are present, the Text node is skipped:

  • Interaction/responder props (onPress, onLongPress, onResponder*, pressRetentionOffset, suppressHighlighting, etc.)
  • selectionColor
  • aria-hidden

id is renamed to nativeID at build time (id wins when both are present). Text bails only if id/nativeID arrive via a spread, or a dynamic id appears alongside a nativeID.

Text structure checks

  • Children must be provably primitive (resolve to a string or number). Anything that could be a React element (nested elements, function calls, unresolved identifiers) bails as contains non-primitive children.

Text ancestor safety checks

Like View, Text optimization depends on ancestor classification:

  • safe: optimize
  • text: skip (has Text ancestor) — a Text nested in another Text renders as the inline NativeVirtualText host (RCTVirtualText), not NativeText (RCTText), so optimizing it would emit the wrong host.
  • unknown: skip by default

Set dangerouslyOptimizeTextWithUnknownAncestors: true to optimize unknown ancestors too.

Dangerous Mode

Enabling dangerous mode can introduce regressions if an unresolved ancestor renders a Text wrapper. A concrete example is expo-router's Link, which wraps its children in a <Text> by default — so a Text under a Link must render as NativeVirtualText, and optimizing it would be wrong.

A Text used as a direct child of expo-router Link with asChild is always skipped (is a direct child of expo-router Link with asChild), even under the dangerous flag, because Link makes that child pressable.

import { Link } from 'expo-router';
import { Text } from 'react-native';

<Link asChild>
  <Text>Open profile</Text>
</Link>;

View Coverage

View and Text share the same ancestor safety checks. The View wrapper translates a few ergonomic props into native equivalents; Boost reproduces each translation at build time (or via a small runtime helper for dynamic values), so these props no longer force a bailout.

View translated props

  • aria-*accessibility* (aria-label, aria-labelledby, aria-live, aria-hidden, and the state/value groups)
  • tabIndexfocusable
  • idnativeID (id wins when both are present)

Everything else — including accessible, accessibilityLabel, and a lone accessibilityState/accessibilityValue — is passed through unchanged.

View prop bailouts

  • A spread that may carry one of the translated props, since Boost cannot reach inside it to translate.
  • A dynamic id alongside a nativeID (their runtime precedence cannot be resolved statically).

Ancestor safety checks

View optimization depends on ancestor classification:

  • safe: optimize
  • text: skip (has Text ancestor)
  • unknown: skip by default

Set dangerouslyOptimizeViewWithUnknownAncestors: true to optimize unknown ancestors too.

Dangerous Mode

Enabling dangerous mode can increase optimization coverage, but it can also introduce regressions if unresolved ancestors render Text wrappers.

Image Coverage

Image optimization is opt-in for now because it uses deprecated React Native deep imports, which may print deprecation warnings. See Configure the Babel Plugin.

The optimizer rewrites supported Image elements when the target platform is known (ios or android) and the source/style/accessibility props can be reproduced safely.

In Unistyles mode, an Image is skipped when its style is (or may be) a Unistyles style.

Spread Props: Resolvable vs Unresolvable

Unresolvable spread props are treated as unsafe and cause bailouts.

// Usually optimizable (resolvable object literal)
<Text {...{ selectable: true }}>Hello</Text>

// Usually skipped (cannot be statically resolved)
<Text {...props}>Hello</Text>

Same rule applies to View.

On this page