You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: apps/docs/content/docs/configuration/configure.mdx
+1-7Lines changed: 1 addition & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,20 +23,14 @@ module.exports = {
23
23
optimizations: {
24
24
text:true,
25
25
view:true,
26
-
image:false,
26
+
image:true,
27
27
},
28
28
},
29
29
],
30
30
],
31
31
};
32
32
```
33
33
34
-
`Image` optimization is currently **opt-in** because it depends on deprecated React Native import paths. Enabling it will log React Native import deprecation warnings to the console. To enable it, explicitly flip it to `true`:
- A Babel plugin that statically analyzes your source code and replaces safe `Text`and `View` components with their direct native counterparts, leading to significant performance improvements compared to the JS-based wrapper components.
12
+
- A Babel plugin that statically analyzes your source code and replaces safe `Text`, `View`, and `Image` components with their direct native counterparts, leading to significant performance improvements compared to the JS-based wrapper components.
13
13
- A runtime package used internally by the plugin for cross-platform-safe imports and helper utilities.
14
14
15
15
The analyzer is intentionally strict and skips any optimizations that may lead to user-facing bugs and behavioral changes.
`Image` source, style, and accessibility props are normalized before Boost swaps the wrapper for
290
+
`NativeImage`. Static values are processed at build time. Dynamic values use small runtime helpers.
291
+
Compared to `Text` and `View`, the host component for `Image` is not imported directly. React Native does not export it through a supported interface, and only exports it through deprecated deep imports which log warnings when used. The plugin therefore registers the `RCTImageView` host directly.
292
+
287
293
For the complete matrix of what's optimized, translated, and skipped, see
The plugin is a single Babel visitor on `JSXOpeningElement`. For each element it runs the `Text`
293
-
optimizer and the `View` optimizer; each follows the same shape.
298
+
The plugin is a single Babel visitor on `JSXOpeningElement`. For each element it runs the `Text`,
299
+
`View`, and `Image` optimizers; each follows the same shape.
294
300
295
301
<Mermaid
296
302
chart={`flowchart TD
297
-
el["JSXOpeningElement (Textor View)"] --> g{"Resolves to the react-native component?"}
303
+
el["JSXOpeningElement (Text, View, or Image)"] --> g{"Resolves to the react-native component?"}
298
304
g -- no --> skip["Leave unchanged"]
299
305
g -- yes --> b{"Does any bailout check fail?"}
300
306
b -- "yes, and no @boost-force" --> skip
301
-
b -- "no, or @boost-force" --> rw["Rewrite props, swap type to NativeText / NativeView"]
307
+
b -- "no, or @boost-force" --> rw["Rewrite props, swap type to its native host"]
302
308
rw --> imp["Inject the runtime import (cached once per file)"]`}
303
309
/>
304
310
@@ -323,9 +329,9 @@ to a string/number; `<Text>{maybeJSX()}</Text>` is not.
323
329
324
330
### Ancestor classification
325
331
326
-
The most intricate check is shared by both optimizers. An element nested inside a `Text`must render as
327
-
the *inline* host (`RCTVirtualText`), not the *block* host — so before optimizing, the plugin walks **up**
328
-
the tree and classifies the ancestor chain as one of:
332
+
The most intricate check is shared by all three optimizers. Components under a `Text`can need different
333
+
host semantics. A nested `Text` uses `RCTVirtualText`, and Android uses a separate inline Image host.
334
+
Before optimizing, the plugin therefore walks **up**the tree and classifies the ancestor chain as one of:
329
335
330
336
-`safe` — no `Text` ancestor anywhere up the chain → optimize.
331
337
-`text` — a `react-native``Text` is an ancestor → skip.
@@ -338,8 +344,8 @@ it can't prove safety, it returns `unknown` and bails. False-positives (a missed
338
344
false-negatives (a regression).
339
345
340
346
The `unknown` case is *often* safe in practice (third-party components rarely wrap children in `Text`), but there are still cases where optimizing components with an `unknown` ancestor could genuinely cause regressions. Therefore, Boost provides
341
-
explicit opt-in escape hatches: `dangerouslyOptimizeViewWithUnknownAncestors` and
342
-
`dangerouslyOptimizeTextWithUnknownAncestors` (see [Configuration](/docs/configuration/configure)).
@@ -353,6 +359,7 @@ even though the visitor fires thousands of times, each runtime symbol is importe
353
359
API lives on the [Runtime Library](/docs/runtime-library) page. The most load-bearing pieces are:
354
360
355
361
-**`NativeText` / `NativeView`** resolve `unstable_NativeText` / `unstable_NativeView` from `react-native` at module load, and **gracefully fall back** to the standard `Text`/`View` on web or any runtime where these exports are missing.
362
+
-**`NativeImage`** loads React Native's public `Image` module to register its host, then renders the registered `RCTImageView` name directly. Web uses the standard `Image` component.
356
363
-**`processTextStyle(style)`** does the same flatten-and-normalize work as the wrapper, with one small difference: it **caches by reference in a `WeakMap`**. When you pass a `StyleSheet.create` reference, the first call flattens it and every later call returns the cached result. The wrapper re-flattens on every render. (Only stable references hit the cache; an inline `style={{…}}` is a fresh reference each render, so it re-flattens either way.)
357
364
-**`processAccessibilityProps(props)`** mirrors `Text`'s `aria-*` translation, `accessibilityState` merge, `disabled` reconciliation, and platform `accessible` default. It runs only when the element actually has accessibility props.
358
365
-**`processViewAccessibilityProps(props)`** does the same for `View`'s ARIA cluster (`aria-labelledby` split, live-region mapping, state/value aggregation, `tabIndex` → `focusable`).
Copy file name to clipboardExpand all lines: apps/docs/content/docs/information/how-it-works.mdx
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,15 +3,15 @@ title: How It Works
3
3
description: Why React Native Boost makes your app faster (the short version).
4
4
---
5
5
6
-
In React Native, `Text`and `View` aren't quite what they appear to be. They look like basic building blocks, but each one is a small JavaScript component that runs **every time it renders**, wrapping a lower-level native component underneath.
6
+
In React Native, `Text`, `View`, and `Image` aren't quite what they appear to be. They look like basic building blocks, but each one is a small JavaScript component that runs **every time it renders**, wrapping a lower-level native component underneath.
7
7
8
-
That wrapper is genuinely useful. It handles a lot of edge cases and powers conveniences like `aria-*`props, `userSelect`, and clamping `numberOfLines`. But most of the time your `Text` or `View` uses none of that, and the wrapper's work is pure overhead. On a busy screen with hundreds of these components, that overhead adds up and starts costing you frames.
8
+
That wrapper is genuinely useful. It handles edge cases and powers conveniences such as accessibility props, style normalization, and Image source processing. But many elements do not need that runtime work, so the wrapper becomes pure overhead. On a busy screen with hundreds of these components, that overhead adds up and starts costing you frames.
9
9
10
10
React Native Boost removes the wrapper when it isn't needed.
11
11
12
12
## The one-sentence version
13
13
14
-
At build time, Boost rewrites `Text`and `View` elements into the native components they were going to render anyway. The work the wrapper used to repeat on every render is either gone completely, or moved from the user's device to build-time.
14
+
At build time, Boost rewrites `Text`, `View`, and `Image` elements into the native components they were going to render anyway. The work the wrapper used to repeat on every render is either gone completely, or moved from the user's device to build-time.
15
15
16
16
## A quick before and after
17
17
@@ -29,7 +29,7 @@ These optimized components are imported from `react-native-boost/runtime` rather
29
29
30
30
## Only when it's safe
31
31
32
-
Boost never changes how your app looks or behaves. It rewrites a component only when it can **prove** that doing so is safe. For each `Text` or `View` it checks a lot of things. For example:
32
+
Boost never changes how your app looks or behaves. It rewrites a component only when it can **prove** that doing so is safe. For each supported component it checks a lot of things. For example:
33
33
34
34
- Is this really the `react-native` component, or some other `Text` from another library?
35
35
- Are its props fully compatible with the underlying native component?
Copy file name to clipboardExpand all lines: apps/docs/content/docs/information/optimization-coverage.mdx
+2-4Lines changed: 2 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ React Native Boost is conservative by design. If it cannot prove an optimization
11
11
| --- | --- | --- |
12
12
|`Text`| Imported from `react-native`, no blacklisted props, primitive children, safe ancestor chain |`contains 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`|
13
13
|`View`| Imported from `react-native`, safe ancestor chain, no spread that may carry a translated prop |`has 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`|
14
-
|`Image`|Opted in with `optimizations.image: true`, imported from `react-native`, native platform known, supported source/style props, safe ancestor chain |`target 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`|
14
+
|`Image`|Imported from `react-native`, native platform known, supported source/style props, safe ancestor chain |`target 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`|
15
15
16
16
## Global Bailouts
17
17
@@ -113,9 +113,7 @@ Set `dangerouslyOptimizeViewWithUnknownAncestors: true` to optimize `unknown` an
113
113
114
114
## Image Coverage
115
115
116
-
`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](/docs/configuration/configure).
117
-
118
-
The optimizer rewrites supported `Image` elements when the target platform is known (`ios` or `android`) and the
116
+
The `Image` optimizer rewrites supported `Image` elements when the target platform is known (`ios` or `android`) and the
119
117
source/style/accessibility props can be reproduced safely.
120
118
121
119
In Unistyles mode, an Image is skipped when its `style` is (or may be) a Unistyles style.
0 commit comments