React Native Optimization Guide 2024
React Native Optimization Guide 2024
TABLE OF CONTENTS
How This Guide Is Organized 3
Introduction to React Native Optimization 6
It’s all about TTI and FPS? 7
PART 1
Pay attention to UI re-renders 12
Use dedicated components for certain layouts 35
Think twice before you pick an external library 45
Always remember to use libraries dedicated to
the mobile platform 51
Find the balance between native and JavaScript 57
Animate at 60FPS – no matter what 64
Replace Lottie with Rive 82
Draw efficiently on a canvas with skia 90
Optimize your app’s JavaScript bundle 102
PART 2
Always run the latest React Native version to access
the new features 109
How to debug faster and better with Flipper 125
Avoid unused native dependencies 131
Optimize your application startup time with Hermes 138
Optimize your Android application’s size with
these Gradle settings 146
Experiment with the New Architecture of React Native 153
PART 3
Run tests for key pieces of your app 165
Have a working Continuous Integration (CI) in place 175
Don’t be afraid to ship fast with Continuous Deployment 186
Ship OTA (Over-The-Air) when in an emergency 199
Make your app consistently fast 208
Know how to profile iOS 221
Know how to profile Android 230
2
The Ultimate Guide to React Native Optimization
Always run the latest React Native version to access the latest
features
4
How This Guide Is Organized The Ultimate Guide to React Native Optimization
This part describes the main problem with React Native performance.
Solution
This part outlines how that problem may affect your business and
what the best practices are to solve it.
Benefits
5
The Ultimate Guide to React Native Optimization
INTRODUCTION TO REACT
NATIVE OPTIMIZATION
The Ultimate Guide to React Native Optimization
The former is a measure of how quickly a user can start using (or
interacting with) your app. TTI is about boot-time performance.
Opening a new app should be fast, period. The latter is a measure
of how snappy your app’s interface is to the user interactions. FPS
is about runtime performance. Using the app should be as smooth
as riding a bike. Maybe unless you turn on that energy saver fea-
ture. The best apps out there, combine great boot-time and run-
time performance to provide the best end-to-end experience for
the users.
Almost every piece of advice you’ll find in this guide will be about
directly or indirectly impacting one of these metrics. And since
React Native gives us the tools to build native Android and iOS
apps sprinkled with some JavaScript on top, there will be lots of
opportunities to impact these metrics from many different angles!
And frankly, it’s not all about these two metrics. After all, if your
app crashes at runtime, can you measure FPS for the interaction
you go after? Optimization is a complex and ongoing process that
needs to happen continuously and on many different grounds for
every successful product. As you progress through this guide,
you’ll gain a better grasp on what affects your users’ experience,
what matters for delivering and perceiving a better performance,
why it matters, and how to solve the challenges that prevent your
beloved users from enjoying the best experience when using your
React Native app.
It’s all about TTI and FPS? The Ultimate Guide to React Native Optimization
9
It’s all about TTI and FPS? The Ultimate Guide to React Native Optimization
Your job is to define the UI components and forget about the rest.
However, that doesn’t mean that you should take the perfor-
mance of your application for granted. In order to create fast and
responsive applications, you have to think the React Native way.
You have to understand how the framework interacts with the un-
derlying platform APIs.
10
The Ultimate Guide to React Native Optimization
PART 1
IMPROVE PERFORMANCE BY
UNDERSTANDING THE IMPLEMENTATION
DETAILS OF REACT NATIVE.
In this section, we will dive deeper into the most popular perfor-
mance bottlenecks and the React Native implementation details
that contribute to them. This will not only be a smooth introduc-
tion to some of the advanced React Native concepts, but it will
also let you improve the stability and performance of your appli-
cation by performing small tweaks and changes.
The following part is focused on the first point from the check-
list of performance optimization tactics: UI re-renders. It’s a very
important part of the React Native optimization process be-
cause it allows for the reduction of the device’s battery usage
which translates into a better user experience for your app.
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 1
PAY ATTENTION TO
UI RE-RENDERS
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
13
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
For this exercise, we’ll use Flipper, a platform for debugging iOS,
Android, and React Native apps. It has React DevTools Profiler in-
tegrated as a plugin that can produce a flame graph of the React
rendering pipeline as a result of profiling. We can leverage this
14
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
useEffect(() => {
setTimeout(() => {
setValue('update 1');
}, 3000);
setTimeout(() => {
setValue('update 2');
}, 5000);
}, []);
return (
<View style={backgroundStyle}>
<ColoredView />
</View>
);
};
const ColoredView = () => {
const style = { backgroundColor: 'red, padding: 10 };
return <View style={style} />;
};
15
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
In the Flipper app, make sure the ''Record why each component
rendered while profiling'' option is enabled in the settings icon and
hit the round blue button to start profiling. After around 5 sec-
onds, hit the round red button to stop profiling. Your profile will
look something like this:
Taking a look at the performance flame graph for the first time
may be slightly intimidating. To understand React DevTools more
in-depth, this video from Ben Awad is good at explaining it. Don’t
forget to watch this talk by Alex at React Native EU, which ex-
plains how we can use flame graph to identify and fix the issues.
Also, visit the official react website for detailed information on
React Profiler.
16
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
return (
<TextInput
accessibilityLabel=''Text input field''
style={styles.textInput}
onChangeText={onChangeText}
value={value}
/>
);
};
The above code sample will work in most cases. However, on slow
devices, and in situations where the user is typing really fast, it
may cause a problem with the view updates.
17
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
As soon as the user starts inputting a new character into the na-
tive input, an update is sent to React Native via the onChangeText
prop (operation 1 on the above diagram). React processes that in-
formation and updates its state accordingly by calling setState .
Next, a controlled component synchronizes its JavaScript val-
ue with the native component value (operation 2 on the above
diagram).
Diagram that shows what happens while typing TEST too fast
18
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
Now, the user was typing fast enough to actually enter anoth-
er character when the value of the text input was set to TE for
a second. As a result, another update arrived (operation 6), with
the value of TET. This wasn’t intentional – the user wasn’t expect-
ing the value of its input to change from TES to TE.
19
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
return (
<View style={styles.container}>
<TextInput
accessibilityLabel=''Text input field''
placeholder=''Type here to translate!''
onChangeText={onChangeText}
defaultValue={value}
style={styles.textInput}
/>
<Text style={styles.label}>
{value
.split(' ')
.map((word) => word && ' ’)
.join(' ')}
🍕
</Text>
</View>
);
};
20
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
GLOBAL STATE
Another common reason for performance issues is how com-
ponents are dependent on the application’s global state. The
worst case scenario is when the state change of a single con-
trol like TextInput or CheckBox propagates the render of
the whole application. The reason for this is a bad global state
management design.
21
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
return (
<FlatList
data={todos}
renderItem={({ item }) => (
<TodoItem name={item.name} completed={item.completed}
/>
)}
keyExtractor={(item) => item.id}
/>
);
};
22
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
const blogPosts = [
{
id: ''post1'',
author: { username: ''user1'', name: ''User 1'' },
body: ''......'',
comments: [
{
id: ''comment1'',
author: { username: ''user2'', name: ''User 2'' },
comment: ''.....'',
},
{
id: ''comment2'',
author: { username: ''user3'', name: ''User 3'' },
comment: ''.....'',
},
],
},
{
id: ''post2'',
author: { username: ''user2'', name: ''User 2'' },
23
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
body: ''......'',
comments: [
{
id: ''comment3'',
author: { username: ''user3'', name: ''User 3'' },
comment: ''.....'',
},
{
id: ''comment4'',
author: { username: ''user1'', name: ''User 1'' },
comment: ''.....'',
},
{
id: ''comment5'',
author: { username: ''user3'', name: ''User 3'' },
comment: ''.....'',
},
],
},
// and repeat many times
];
const blogPosts = {
posts: {
byId: {
post1: {
id: ''post1'',
author: ''user1'',
body: ''......'',
comments: [''comment1'', ''comment2''],
},
post2: {
id: ''post2'',
author: ''user2'',
body: ''......'',
comments: [''comment3'', ''comment4'', ''comment5''],
},
},
allIds: [''post1'', ''post2''],
},
24
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
comments: {
byId: {
comment1: {
id: ''comment1'',
author: ''user2'',
comment: ''.....'',
},
comment2: {
id: ''comment2'',
author: ''user3'',
comment: ''.....'',
},
comment3: {
id: ''comment3'',
author: ''user3'',
comment: ''.....'',
},
comment4: {
id: ''comment4'',
author: ''user1'',
comment: ''.....'',
},
comment5: {
id: ''comment5'',
author: ''user3'',
comment: ''.....'',
},
},
allIds: [''comment1'', ''comment2'', ''comment3'',
''comment4'', ''comment5''],
},
users: {
byId: {
user1: {
username: ''user1'',
name: ''User 1'',
},
user2: {
username: ''user2'',
name: ''User 2'',
},
user3: {
username: ''user3'',
name: ''User 3'',
25
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
},
},
allIds: [''user1'', ''user2'', ''user3''],
},
};
import {
createEntityAdapter,
createSlice,
PayloadAction,
nanoid,
} from ''@reduxjs/toolkit'';
import { RootState } from ''../../store'';
26
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
27
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
ATOMIC STATE
The pattern of a single store – a top-down mental model – as ini-
tially promoted by Redux is moving towards a more atomic design.
Let’s say we have a TODO list with some filters: ''show all'', ''just
the active ones'', or ''just the completed ones''. We identify the top
component, the TodoList , but we don’t start here. We first iden-
tify the children and start building those smaller components, so
that later we can mix them and build a complex element. We need
to make some data visible in one component which will be man-
aged by another one. To avoid a parent state and passing down
the data and actions (top-down), we are going to use a state
manager. This state manager will be in charge of storing the data,
making it accessible, and providing actions/modifiers, because we
28
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
Zustand
Store
We create the obj where the filter will be and the modifiers will
be exposed:
Here we update the ticket and this item so any children won’t suf-
fer a re-render:
29
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
Todo item
return (
<View>
<Text>{item.title}</Text>
<Text>{item.description}</Text>
</View>
);
};
Jotai
Jotai was built with this bottom-up approach in mind, so its syntax
is minimal and simple. Using the concept of atom as a ''store'' then
you use different hooks to make the atom readonly or mutable.
Store
FilterMenuItem
30
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
TodoItem
if (!shouldBeShown(filter, item.done)) {
return null;
}
return (
<View>
<Text>{item.title}</Text>
<Text>{item.description}</Text>
</View>
);
};
The name React Forget comes from the principle with which this
tool is supposed to work. The component level optimisations and
memoization will be taken care of by the compiler, so you can
''forget'' about all the overhead and extra boilerplate that came
with doing this by hand.
31
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
Let’s take a look at the example code for todo list application from
the previous chapter about atomic state and see how it can be
simplified with the future Forget compiler.
return (
<View>
<FlatList data={todos} renderItem={MemoizedTodo} />
<Text>Done: {doneTodosNumber}</Text>
<TouchableOpacity onPress={handleChange}>
<Text>Change</Text>
</TouchableOpacity>
</View>
);
};
32
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
and it will behave exactly the same as the previous example with
manual memoization.
As authors of this book, we’re all excited for React Forget and
can’t wait when it ships. Hopefully we’ll be able to remove most of
the topics related to manual memory memoization management.
33
Pay attention to UI re-renders The Ultimate Guide to React Native Optimization
understand them and pick the tool you’re most productive with.
For some, it may be the verbosity of Redux. But others may pre-
fer the ease of Zustand that avoids having to think about extra
re-renders.
With all these steps in mind, your application should perform few-
er operations and need smaller resources to complete its job. As
a result, this should lead to lower battery usage and more satis-
faction from interacting with the interface.
34
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 2
USE DEDICATED
COMPONENTS FOR
CERTAIN LAYOUTS
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
36
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
🍏🍊🥑
const objects = [
['avocado', ' '],
['apple', ' '],
['orange', ' '],
['cactus', ' '],🌵🍆
🥥🍓
['eggplant', ' '],
['strawberry', ' '],
['coconut', ' '],
37
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
];
const getRandomItem = () => {
const item = objects[~~(Math.random() * objects.length)];
return {
name: item[0],
icon: item[1],
id: Date.now() + Math.random(),
};
};
return (
<View style={styles.container}>
<Button title=''add item'' onPress={addItem} />
<ScrollView>
{items.map(({ name, icon, id }) => (
<View style={styles.itemContainer} key={id}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.icon}>{icon}</Text>
</View>
))}
</ScrollView>
</View>
);
};
38
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
🍏🍊🥑
const objects = [
'avocado ''
'apple ''
'orage
'cactus 🌵🍆
''
''
🥥🍓
'eggplant ''
'strawberry ''
'coconut ''
];
const getRandomItem = () => {
const item = objects[~~(Math.random() * objects.length)].
split(' ');
return {
name: item[0],
icon: item[1],
id: Date.now() + Math.random(),
};
};
return (
<View style={styles.container}>
<Button title=''add item'' onPress={addItem} />
<FlatList
data={items}
keyExtractor={keyExtractor}
renderItem={renderItem}
/>
</View>
);
};
39
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
Well, the key lies in the logic that is abstracted away within
the FlatList component. It contains a lot of heuristics and ad-
vanced JavaScript calculations to reduce the amount of extra-
neous renderings that happen while you're displaying the data
on screen and to make the scrolling experience always run at
60FPS. Just using FlatList may not be enough in some cas-
es. FlatList performance optimizations rely on not rendering
elements that are currently not displayed on the screen.
40
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
to render the next batch of data, it will have to wait for all the new
items to render to measure their height.
41
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
There are a couple of props that are quite important with FlashList .
First is estimatedItemSize , the approximate size of the list
item. It helps FlashList to decide how many items to ren-
der before the initial load and while scrolling. If we have differ-
ent-sized items, we can average them. We can get this value
in a warning by the list, if we do not supply it on the first ren-
der and then use it forward. The other way is to use the ele-
ment inspector from the dev support in the React Native app.
The second prop is overrideItemLayout , which is prioritized
over estimatedItemSize . If we have different-sized items and
we know their sizes, it's better to use them here instead of aver-
aging them.
42
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
43
Use dedicated components for certain layouts The Ultimate Guide to React Native Optimization
44
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 3
This type of ecosystem has many advantages, but also some se-
rious drawbacks. One of them is that developers can find it hard
to choose from multiple libraries supporting the same use case.
When picking the one to use in the next project, they often re-
search the indicators that tell them if the library is healthy and well
maintained, such as GitHub stars, the number of issues, contrib-
utors, and PRs.
46
Think twice before you pick an external library The Ultimate Guide to React Native Optimization
Although you will not be able to do much about the device limita-
tions, you can control your JavaScript code. In general, less code
means faster opening time. And one of the most important factors
affecting the overall size of your code is libraries.
This means that all the code that you pull from NPM and import to
your project will be present in your production JS bundle, load-
ed into memory, and parsed.That can have a negative impact on
the total startup time of your application.
47
Think twice before you pick an external library The Ultimate Guide to React Native Optimization
What's worth pointing out is that it's not the case with Hermes
engine, which automatically pages only necessary bytecode into
memory. Read more in the Hermes chapter.
If you are about to pull a complex library, check if there are smaller
alternatives that have the functionality you're looking for.
48
Think twice before you pick an external library The Ultimate Guide to React Native Optimization
time. Rather than pulling down the entire moment.js library (67.9
KB) to parse the date itself,
49
Think twice before you pick an external library The Ultimate Guide to React Native Optimization
As a result, you can benefit from the utilities that are a part of
the lodash package without pulling them all into the application
bundle.
50
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 4
52
Always remember to use libraries dedicated to the mobile platform The Ultimate Guide to React Native Optimization
Firebase contains SDKs for the web and mobile – iOS and Android
respectively. Each SDK contains support for Realtime Database.
53
Always remember to use libraries dedicated to the mobile platform The Ultimate Guide to React Native Optimization
Thanks to React Native, you can run the web version of it without
major problems:
However, this is not what you should be doing. While the above
example works without issues, it does not offer the same perfor-
mance as the mobile equivalent. The SDK itself also contains few-
er features – no surprises here, as web is different and theres
no reason Firebase. js should provide support for mobile features.
54
Always remember to use libraries dedicated to the mobile platform The Ultimate Guide to React Native Optimization
As you can see, the difference is minimal. In this case, the library
authors did a great job mimicking the API to reduce the potential
confusion while switching back and forth between the web and
mobile context.
55
Always remember to use libraries dedicated to the mobile platform The Ultimate Guide to React Native Optimization
For advanced use cases, you can easily extend React Native with
a native functionality and talk directly to the mobile SDKs. Such
an escape hatch is what makes React Native extremely versatile
and enterprise-ready. It allows you to build features faster on
many platforms at once, without compromising on the performance
and user experience – something other hybrid frameworks can-
not claim.
56
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 5
As a result, JavaScript can execute native APIs and pass the nec-
essary context to receive the desired return value. The commu-
nication itself is asynchronous – it means that while the caller is
waiting for the results to arrive from the native side, the JavaScript
is still running and may already be up for another task.
58
Find the balance between native and JavaScript The Ultimate Guide to React Native Optimization
The number of JavaScript calls that arrive over the bridge is not
deterministic and can vary over time, depending on the number of
interactions that you do within your application. Additionally, each
call takes time, as the JavaScript arguments need to be stringified
into JSON, which is the established format that can be understood
by these two realms.
For example, when the bridge is busy processing the data, another
call will have to block and wait. If that interaction was related to
gestures and animations, it is very likely that you have a dropped
frame – the operation wasn't performed causing jitters in the UI.
59
Find the balance between native and JavaScript The Ultimate Guide to React Native Optimization
60
Find the balance between native and JavaScript The Ultimate Guide to React Native Optimization
In such a scenario, you have lost some time waiting for the excep-
tion that you could've checked for beforehand.
ToastExample.show(message, duration);
};
61
Find the balance between native and JavaScript The Ultimate Guide to React Native Optimization
This is the case when styling components like e.g. View or Text
using the style prop. Let's take a look at the following example
using inline styles.
62
Find the balance between native and JavaScript The Ultimate Guide to React Native Optimization
In React Native we have nicer ways to deal with styling and it's
through StyleSheet API – a dedicated abstraction similar to CSS
StyleSheets. Although it provides no performance benefits, it's
worth calling it out for the ease of development and maintenance.
When we develop our app in TypeScript or Flow, StyleSheet is well
typed and makes it possible for our code editors to auto-complete.
63
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 6
ANIMATE AT 60FPS –
NO MATTER WHAT
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
With React Native, this story is a bit different. If you do not think
about your animations top-down beforehand and choose the right
tools to tackle this challenge, you're on track to run into dropped
frames sooner or later.
65
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
Mobile users like the interfaces that follow them along and that look
top-notch and ensure the animations are always running smoothly
is a fundamental part that builds such an experience.
Enabling the usage of the native driver is the easiest way of quickly
improving your animations' performance. However, the subset of
style props that can be used together with the native driver is lim-
ited. You can use it with non-layout properties like transforms and
opacity. It will not work with colors, height, and others. Those are
enough to implement most of the animations in your app because
you usually want to show/hide something or change its position.
// [...]
66
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
For more complex use cases, you can use the React Native
Reanimated library. Its API is compatible with the basic Animated
library and introduces a set of fine-grained controls for your an-
imations with a modern hooks-based interface. More importantly,
it introduces the possibility to animate all possible style props with
the native driver. So animating height or color will no longer be
an issue. However, transform and opacity animations will still be
slightly faster since they are GPU-accelerated. You can play with
different combinations in this reanimated playground.
GESTURE-DRIVEN ANIMATIONS
The most desired effect that can be achieved with animations is
being able to control animation with a gesture. For your customers,
this is the most enjoyable part of the interface. It builds a strong
sentiment and makes the app feel very smooth and responsive.
Plain React Native is very limited when it comes to combining
gestures with native driven animations. You can utilize ScrollView
scroll events to build things like a smooth collapsible header.
67
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
return (
<PanGestureHandler onGestureEvent={eventHandler}>
<Animated.View style={animatedStyle}>{props.children}</
Animated.View>
</PanGestureHandler>
);
};
68
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
69
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
return (
<View style={styles.container}>
<BottomSheet
callbackNode={gestureCallbackNode}
snapPoints={[50, 400]}
initialSnap={1}
renderHeader={renderHeader}
renderContent={renderInner}
/>
</View>
);
};
export default Example;
70
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
const ExpensiveTaskStates = {
notStared: 'not started',
scheduled: 'scheduled',
done: 'done',
};
71
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
useNativeDriver: false,
}).start(() => {
setAnimationState((prev) => !prev);
});
setExpensiveTaskState(ExpensiveTaskStates.scheduled);
InteractionManager.runAfterInteractions(() => {
setExpensiveTaskState(ExpensiveTaskStates.done);
});
};
return (
<View style={styles.container}>
{Platform.OS === 'web' ? (
❗
<Text style={styles.infoLabel}>
InteractionManager works only on native platforms.
Open example on
❗
iOS or Android
</Text>
) : (
<>
<Button
title=''Start animation and schedule expensive
task''
onPress={startAnimationAndScheduleExpensiveTask}
/>
<Animated.View
style={[styles.box, { width: animationValue.cur-
rent }]}>
<Text>Animated box</Text>
</Animated.View>
<Text style={styles.paragraph}>
Expensive task status: {expensiveTaskState}
</Text>
</>
)}
</View>
);
};
72
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
textAlign: 'center',
},
});
USING REANIMATED
One of the main benefits of using React Native Reanimated is its
ability to perform animations directly on the native thread rather
than the JavaScript thread. This can lead to significant perfor-
mance improvements and smoother animations, especially on
73
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
74
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
backgroundColor: interpolateColor(
animatedValue.value,
[0, 1],
['gold', 'salmon']
),
borderColor: interpolateColor(
animatedValue.value,
[0, 1],
['purple', 'papayawhip']
),
borderWidth: 10,
};
}, [animatedValue.value]);
return (
<View style={styles.container}>
<Animated.View style={animatedStyle} />
</View>
);
}
75
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
useDerivedValue
It’s a shared value but used when deriving or creating a new value
out of a shared value. This is useful when you’d like to abstract
some logic, like interpolate, some Math operations or for reusabil-
ity purposes.
useAnimatedStyle
useAnimatedProp
76
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
interpolate
This method will let you remap values from on inputRange and
outputRange
interpolateColor
Timing functions
anim.value = withRepeat(
withSequence(
withDelay(1000, withTiming(1)
withDelay(1000, withTiming(0)
)
Infinity
true
)
77
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
UI Primitives
78
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
We can see that there’s the same interpolate with the same out-
putRange for width, height and transform.translateX, instead, we
can move it to a derived value:
In this way, we abstract and reuse the code, less error-prone code
and ultimately more performant, because the `interpolatedValue`
it’s calculated only once.
function MyComponent() {
const width = useSharedValue(50);
const height = useSharedValue(50);
const borderRadius = useSharedValue(0);
const backgroundColor = useSharedValue(0);
const translateY = useSharedValue(0)
79
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
React.useEffect(() => {
width.value = withTiming(100);
height.value = withTiming(100);
borderRadius.value = withTiming(50);
backgroundColor.value = withTiming(1);
translateY.value = withTiming(50)
}, [])
function MyComponent() {
// Define the shared animated value
const animatedValue = useSharedValue(0);
React.useEffect(() => {
// Start the animation
animatedValue.value = withTiming(1);
}, []);
80
Animate at 60FPS – no matter what The Ultimate Guide to React Native Optimization
return {
layout,
onLayout,
};
};
81
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 7
83
Replace Lottie with Rive The Ultimate Guide to React Native Optimization
Source: https://lottiefiles.com/128635-letter-d
We can still do better, but how good will that be if we use a sin-
gle animation and control it using triggers? For example, if we tap
on a button, the state of the animation changes for the next step.
Let's say we want to change the size or do other customizations,
we can't do it in the editor provided by LottieFiles. Instead, we
will have to import that in Adobe AE and then do the adjustments
and re-export it as JSON. We can, however, customize the color
for layers in the web editor. Not everyone has expertise in using
Adobe AE and has an animator to consult, e.g. if we're working on
our pet project, we will want to adjust any animation in the web
editor.
84
Replace Lottie with Rive The Ultimate Guide to React Native Optimization
85
Replace Lottie with Rive The Ultimate Guide to React Native Optimization
return (
<SafeAreaView>
<Rive
ref={riveRef}
resourceName=''character''
style={styles.character}
stateMachineName={stateMachineOne}
autoplay
/>
<Button title=''Is Running'' onPress={onIsRunning} />
<Button title=''SideKick'' onPress={onSideKick}
color=''#3e3e3e'' />
</SafeAreaView>
);
};
Now let's talk about performance in terms of FPS, CPU, and mem-
ory consumption. We will do a comparison of an animation that is
built for Lottie with the same animation built for Rive. This tweet
shows benchmarks done for the web platform. We'll extend this
by using the same animations for Rive and Lottie on our React
Native app.
86
Replace Lottie with Rive The Ultimate Guide to React Native Optimization
Lottie
Rive
Both images show a tiny window right above the rocket with
details of FPS on both the UI and JS thread. The results show
87
Replace Lottie with Rive The Ultimate Guide to React Native Optimization
that the Rive animation runs almost at 60FPS whereas Lottie runs
at 17FPS.
Now let's focus on the right part of both images, which is the Memory
consumption detailed view. If we look closely, there are main-
ly three portions: Java, Native, and Graphics. Java represents
the memory allocated for Java or Kotlin code. The Native rep-
resents the memory allocated from C or C++ code. The graphics
represent the memory used for the graphics buffer queues to dis-
play pixels on the screen. In Java, Lottie uses almost 23 MB and
Rive uses almost 12 MB of RAM. In Native, Lottie uses 49 MB and
Rive uses 25 MB of RAM. In Graphics, Lottie consumes 123 MB,
whereas Rive uses 184 MB of RAM. The total memory consump-
tion of Lottie is 246 MB and Rive is 276 MB.
88
Replace Lottie with Rive The Ultimate Guide to React Native Optimization
that will interact after being given a certain input. Now the devel-
oper can use that animation and implement the interactivity firing
the inputs on the animation and be done with it. If this animation
needs to be changed with the same inputs, the dev only needs to
replace the animation source and that's it. More info here.
89
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 8
DRAW EFFICIENTLY ON
A CANVAS WITH SKIA
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
While being easy to use and performant tool, Rive also has some
constraints like limited Blur, Glow, Shadow support and limited
path effects.
91
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
the pixels. It's a powerful instrument to cover almost any case you
can imagine with the great performance overall.
React Native Skia's API uses the <Canvas /> element that will be
the last '’’'native'' element in the app's view tree and will serve as
a root of your Skia drawing. All react-native-skia child components
that will be put into it will serve as a declarative api for the library.
Behind the scenes it'll use its own React renderer.
92
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
From this example we also can see one of the core elements of
the API – the <Group /> component. Groups can be deeply nest-
ed and can apply operations to their children:
• Paint properties – pretty similar to svg the properties applied
to the group (ex. style, color) will be inherited by the child
elements.
• Transformations – almost identical to React Native trans-
form property with one significant difference: in React Native,
the origin of transformation is the center of the object, whereas
it is the top-left position of the object in Skia.
• Clipping – clip property provides the region in which children
elements will be shown while outside region's pert will be hid-
den. It can be reverted invertClip property.
• Bitmap effects – layer property will create bitmap drawing of
the children which you can for example use to build effects
that need to be applied to the group of elements.
93
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
94
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
As you might have noticed, React Native Skia can directly accept
Reanimated values as props, making it seamless to integrate can-
vas drawings with animations that delight your users.
95
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
<Group>
<Circle cx={r} cy={c.y} r={r} color=''cyan'' />
<Circle cx={width - r} cy={circleTranslate} r={r}
color=''magenta'' />
</Group>
</Canvas>
</>
);
};
The thing to remember is that if you are using the <Canvas /> to
apply the effect that will need to affect the underlying layer these
changes will only be applied to the <Canvas /> elements. Things
like <Shadow /> will be rendered correctly but <Blur /> will
only have the data about the elements that are used inside can-
vas. You will need to somehow capture the snapshot of the layer.
const pd = PixelRatio.get();
const App = () => {
const {width, height} = useWindowDimensions();
const c = vec(width / 2, height / 2);
const r = width * 0.33;
const CARD_WIDTH = width - 60;
const CARD_HEIGHT = CARD_WIDTH * 0.5;
const ANIMATION_OFFSET = 50;
const clip = useMemo(
() => rrect(rect(0, 0, CARD_WIDTH, CARD_HEIGHT), 20, 20),
[CARD_HEIGHT, CARD_WIDTH],
);
const ref = useRef<View>(null);
96
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
useEffect(() => {
makeImageFromView(ref).then(snapshot =>
setImage(snapshot));
}, []);
return (
<>
<View
ref={ref}
collapsable={false}
style={{
alignItems: 'center',
justifyContent: 'center',
...StyleSheet.absoluteFill,
}}>
<View style={{height: 300, width: 300,
backgroundColor: 'red'}} />
</View>
<Canvas style={{flex: 1}}>
<Image
image={image}
x={0}
y={0}
height={(image?.height() || 0) / pd}
width={(image?.width() || 0) / pd}
/>
<Group>
<Circle cx={r} cy={c.y} r={r} color=''cyan'' />
<Circle cx={width - r} cy={circleTranslate} r={r}
color=''magenta'' />
97
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
</Group>
<BackdropBlur blur={20} transform={transform}
clip={clip}>
<Fill color=''rgba(0, 0, 0, 0.3)'' />
</BackdropBlur>
</Canvas>
</>
);
};
98
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
99
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
100
Draw efficiently on a canvas with skia The Ultimate Guide to React Native Optimization
and contain the same number and types of commands or the in-
terpolation may potentially look incorrect or can cause the crash
of your app.
The best places to learn more about React Native Skia and possible
applications will be the official documentation, William Candillon's
YouTube channel, and Daniel Friyia's YouTube channel.
101
The Ultimate Guide to React Native Optimization
PART 1 | CHAPTER 9
Re.Pack
Re.Pack is a Webpack-based toolkit to build your React Native
application with full support of the Webpack ecosystem of load-
ers, plugins, and support for various features like symlinks, aliases,
code splitting, etc. Re.Pack is the successor to Haul, which served
a similar purpose but balanced a different set of tradeoffs and
developer experience.
103
Optimize your app’s JavaScript bundle The Ultimate Guide to React Native Optimization
Another Webpack feature that helps our apps achieve better per-
formance is reducing the amount of code in the final bundle with
tree shaking. Tree shaking is a dead code elimination technique
done by analyzing the import and export statements in the source
code and determining which code is actually used by the appli-
cation. Webpack will then remove any unused code from the fi-
nal bundle, resulting in a smaller and more efficient application.
The code that’s eligible for tree shaking needs to be written in
ECMAScript modules (import and export statements) and mark itself
as side-effect free through package.json sideEffects: false
clause.
Webpack has support for symlinks but since React Native 0.72,
Metro offers that as well in an experimental form. And since v0.73
it’s turned on by default. Symlinks prove useful inside of monore-
pos, where node modules can be optimally shared between dif-
ferent workspaces.
104
Optimize your app’s JavaScript bundle The Ultimate Guide to React Native Optimization
a remote server or a CDN. Now this can help you with reducing
not only the initial load time, but also the precious app size. It also
opens up a way to Over-The-Air (OTA) updates that target only
a small part of your app.
If you don’t need the huge customization that the Webpack eco-
system offers or don’t plan to split your app code, then you may
as well keep the default Metro bundler.
react-native-esbuild
105
Optimize your app’s JavaScript bundle The Ultimate Guide to React Native Optimization
.cts files, which means ESBuild has built-in support for parsing
TypeScript syntax and discarding the type annotations. However,
ESBuild does not do any type checking so you will still need to
run type check in parallel with ESBuild to check types. This is not
something ESBuild does itself.
rnx-kit
106
Optimize your app’s JavaScript bundle The Ultimate Guide to React Native Optimization
107
The Ultimate Guide to React Native Optimization
PART 2
In this section, we will show you some of the features you can
turn on right now to start your optimization process. We also en-
courage you to keep track of all the new React Native features to
make sure you use the framework to its full potential.
The Ultimate Guide to React Native Optimization
PART 2 | CHAPTER 1
Every day, developers from all around the world introduce new
features, critical bug fixes, and security patches. On average, each
release includes around 500 commits.
FAST REFRESH
To improve the developer experience and velocity, the React team
introduced a feature called Fast Refresh to React Native. This lets
you quickly reflect the code on the device, whenever you save
the file instead of building or reloading the app. It is smart enough
110
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
AUTO-LINKING
Whenever we add native code to our React Native app as a depen-
dency, it needs to be linked. Previously linking was done manually
or using react-native link dep-name. React Native CLI introduced
auto-linking so the devs didn't need to link a library themselves.
After a couple of years, when the re-architecture of React Native
was released to the community, a need arose to auto link fabric
components and turbo modules, which was handled gracefully by
the CLI team. They released an update to the community to help
the developer experience and velocity.
FLIPPER
Important note: Debugging React Native apps with Flipper is dep-
recated in React Native 0.73. The out-of-the box support will
eventually be removed for JS debugging via Flipper in 0.74.
111
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
You can opt-in to the new debugger using the start 's com-
mand --experimental-debugger flag:
112
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
Now it's just a case of hitting the j key and this will launch the new
debugger using Google Chrome or Microsoft Edge.
Dev tools plugins are snippets that create a bridge between your
app and Chrome window. These bridges open up a world of pos-
sibilities for app inspection and debugging, making the process
both simpler and more efficient.
Available plugins
While this is a relatively new feature, Expo already has some built-
in dev tool plugins that are already available to use:
• React Navigation Plugin: Ideal for apps utilizing React Navigation
or Expo Router, allows rewinding navigation history and even
sending deep links.
• Apollo Client Plugin: Useful for apps using Apollo Client, pro-
viding insights into query and mutation history, and cache
management.
113
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
Integrating a plugin
After installation, add the necessary code snippet to your app's root
component. This setup ensures seamless two-way communication
between your app and the plugin, enriching your development and
debugging process.
In this example, we'll pass the navigation root to the plugin in our
app's entry point when running in development mode when using
React Navigation:
114
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
useReactNavigationDevTools(navigationRef);
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</
NavigationContainer>
);
}
You can also use the same plugin when you navigate with Expo
Router since it also uses React Navigation under the hood. However,
the setup is slightly different:
Once these code changes are applied, open your terminal, run
npx expo start, press shift + m to open the list of dev tools, and
then select the React Navigation plugin. This will open the plugin's
web interface, showing your navigation history as you navigate
through your app.
115
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
If you haven't found a plugin to fit your use case yet, you can also
build your own.
LOGBOX
React Native redesigned its error and warning handling system.
They had to do a ground-up redesign of its logging system and
the developer experience is much better because of it. Developers
can easily trace the cause of an error using code frames and
component stacks. Syntax error formatting helps to understand
the issue more quickly with the aid of syntax highlighting. Log
Notifications show warnings and logs at the bottom of the screen
instead of covering the whole screen.
HERMES
A new JS engine created by Meta to improve the performance
of React Native apps in terms of CPU usage, memory consump-
tion, app size, and Time To Interactive (TTI). Initial support was
launched for Android devices, but after two years, support was
extended to iOS as well. After a couple of months, the previous-
ly used garbage collector for Hermes GenGC was replaced with
a new one called Hades – a concurrent garbage collector. The
Meta team saw improvements of CPU-intensive workloads by
20-50%. Later on, the team decided to ship a bundled Hermes
116
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
NEW ARCHITECTURE
This one has its own chapter.
In the React Native ecosystem, it's common that libraries are not
backward – compatible. New features often use goodies not avail-
able in the previous versions of the framework. This means that if
your application runs on an older version of React Native, you are
eventually going to start missing out on the latest improvements.
117
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
For instance, it may turn out that the modules and components
you used in your code are no longer part of the react-native core.
118
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
119
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
Having a better overview of the changes will help you move faster
and act with more confidence.
120
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
Thanks to that, you will not only be aware of the changes, but you
will also understand the reasoning behind them. And you will be
ready to open up your project and start working on it.
121
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
The first step is to bump the React and React Native dependen-
cies to the desired versions and perform the necessary chang-
es (including breaking changes). To do so, you can look up
the suggestions provided by React Native Upgrade Helper and
apply them manually. Once it's completed, make sure to reinstall
your node_modules .
122
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
If the error occurs during the build time, bumping the depen-
dency to its latest version usually makes it work. But it may not
always be the case. To make sure the version of React Native
you're upgrading to is compatible with your dependencies, use
the align-deps project by Microsoft developers. It allows you to
keep your dependencies on the right version based on the re-
quirements and by leveraging the presets of rules. It also has
a CLI, so you can wire it up to your CI and ensure that no one in
your repo or monorepo will inadvertently introduce incompatible
versions of packages and break the app.
Once your application builds, you are ready to check the chan-
gelog and make yourself familiar with the JavaScript changes
that happened to the public API. If you overlook this step, it can
result in runtime exceptions. Using Flow or TypeScript should help
you ensure that the changes were applied properly.
As you can see, there is no magic trick that would fix all the er-
rors and upgrade the dependencies automatically. This is mostly
manual work that has to be done with patience and attention. It
also requires a lot of testing to ensure that you didn't break any
features along the way. Fortunately, there are tools like align-deps
that help you avoid at least some of the manual work, improving
the upgrading experience significantly.
123
Always run the latest React Native version to access the new features The Ultimate Guide to React Native Optimization
Upgrades like this are really critical to keeping your users satisfied.
After all, they would be disappointed if the app started to crash
with the newer version of the operating system or disappeared
from the App Store. There might be some additional workload as-
sociated with every release, but staying up to date will pay back
with happier users, more stable apps, and a better development
experience.
124
The Ultimate Guide to React Native Optimization
PART 2 | CHAPTER 2
When it comes to debugging native code, you have to use the tools
built into Android Studio and Xcode.
126
How to debug faster and better with Flipper The Ultimate Guide to React Native Optimization
127
How to debug faster and better with Flipper The Ultimate Guide to React Native Optimization
From the developer menu, you can access other debugging util-
ities, such as layout inspector or performance monitor. The latter
is relatively convenient to use, as it’s displaying only a small piece
of information. However, employing the former is a struggle be-
cause of the limited workspace it provides.
128
How to debug faster and better with Flipper The Ultimate Guide to React Native Optimization
2019, Flipper has been shipped by default with React Native since
version 0.62.
Source: https://fbflipper.com/docs/features/react-native
What’s more, Flipper lets you preview logs from native code and
track native crashes, so you don’t have to run Android Studio or
Xcode to check what is happening on the native side!
129
How to debug faster and better with Flipper The Ultimate Guide to React Native Optimization
What’s most exciting is that all the needed utilities are placed
in one desktop app. This minimizes context switches. Without
Flipper, a developer debugging an issue related to display-
ing the data fetched from the backend had to use the Chrome
Debugger (to preview logs), in-emulator network requests debug-
ger, and probably in-emulator layout inspector, or a standalone
React Devtools app. With Flipper, all those tools are available as
built-in plugins. They are easily accessible from a side panel and
have similar UI and UX.
130
The Ultimate Guide to React Native Optimization
PART 2 | CHAPTER 3
132
Avoid unused native dependencies The Ultimate Guide to React Native Optimization
to find the actual code that’s associated with the third-party de-
pendency we want our app to use. What’s important is that it’s
necessary and for a long time we, React Native developers, need-
ed to do this step manually. Since React Native 0.60, we have
an automated way of doing this thanks to the React Native CLI.
133
Avoid unused native dependencies The Ultimate Guide to React Native Optimization
Unused dependencies
* lottie-react-native
* react-native-gesture-handler
* react-native-maps
* react-natave-reanimated
* react-native-video
* react-native-webview
Unused devDependencies
* @babel/core
* @babel/runtime
* @react-native-community/eslint-config
* @tsconfig/react-native
* @types/jest
* @types/react-test-renderer
* babel-jest
* jest-circus
* metro-react-native-paper-preset
* typescript
Dev dependencies likely won’t end up in the JS bundle, but could still
link native code into your production app if they have native code
in their implementationIn this example, the dev Dependencies list-
ed are JS-only, so there is no need to focus on them. The results
show us that we have a few unused dependencies – and what’s
more important, in this example, these dependencies are rely-
ing on some native code. Now we have to remove them and it’s
done! In the example app, removing unused dependencies from
the screenshot above occurred with the following reduction in
the application size:
Comparision of bundle sizes before and after removing unused native dependencies
Possibly even more than reducing the application size, , there was
a noticeable improvement in the Time to Interactive on the tested
Android device, which was reduced by 17% in this case.
You may be wondering how you can measure the TTI in your appli-
cation. There are a few ways to do it. Whichever you choose, re-
member to always measure on a release version of the app when
dealing with absolute numbers.
134
Avoid unused native dependencies The Ultimate Guide to React Native Optimization
One way is to use a stopwatch and measure the time the app took
to show the first screen, as shown here. It’s entirely manual, but
it will often get the job done for one-off measurements. Another
manual way is to use a recent phone that has a high-frame rate
camera (eg 120fps), and record a video of your app launch on
a real device. You can then load the video, zooming into the time-
line to the exact time offsets between tapping your app icon and
when the first meaningful render happens. We have used this da-
ta-driven method to accurately and repeatedly observe improve-
ments as small as 50ms, which may sound small, but can often
be the difference between an adequate experience for the user
versus a magical one.
We can also use App launch from Xcode instruments, but you
should note that this is not the same as the end-user experience
on their device. You should always double-check your production
application build on a retail device as to as possible to what your
users have. All you need is to install a release build through pro-
filing to your real device. Then select App Launch from the win-
dow that will appear automatically once the build is installed. Hit
135
Avoid unused native dependencies The Ultimate Guide to React Native Optimization
the record button, and once the app has launched, stop recording.
You will get an output similar to this:
There are two phases when calculating app launch time on iOS.
The first one is called pre-main time, and it’s the time before
the main function of the app is executed. It’s marked with the pur-
ple area on the graph above – all the work needed to launch
the app correctly, like initialization and the linking of libraries hap-
pens in this phase.
136
Avoid unused native dependencies The Ultimate Guide to React Native Optimization
137
The Ultimate Guide to React Native Optimization
PART 2 | CHAPTER 4
OPTIMIZE YOUR
APPLICATION STARTUP
TIME WITH HERMES
Optimize your application startup time with Hermes The Ultimate Guide to React Native Optimization
There are quite a few things that happen from the moment you
press the application icon from the drawer for the first time.
139
Optimize your application startup time with Hermes The Ultimate Guide to React Native Optimization
As you can see in the diagram below, there are two groups of op-
erations that influence the overall startup time of your application.
The first one involves the first two operations (1 and 2 in the di-
agram above) and describes the time needed for React Native
to bootstrap (to spin up the VM and for the VM to execute
the JavaScript code). The other one includes the remaining op-
erations (3 and 4 in the diagram above) and is associated with
the business logic that you have created for your application. The
length of this group is highly dependent on the number of compo-
nents and the overall complexity of your application.
If you have not measured the overall startup time of your applica-
tion or have not played around with things such as Hermes yet –
keep on reading.
140
Optimize your application startup time with Hermes The Ultimate Guide to React Native Optimization
141
Optimize your application startup time with Hermes The Ultimate Guide to React Native Optimization
BYTECODE PRECOMPILATION
Typically, the traditional JavaScript VM works by parsing
the JavaScript source code during the runtime and then produc-
ing the bytecode. As a result, the execution of the code is delayed
until the parsing completes. It is not the same with Hermes. To
reduce the time needed for the engine to execute the business
logic, it generates the bytecode during the build time.
142
Optimize your application startup time with Hermes The Ultimate Guide to React Native Optimization
It can spend more time optimizing the bundle using various tech-
niques to make it smaller and more efficient. For example, the gen-
erated bytecode is designed in a way so that it can be mapped
in the memory without eagerly loading the entire file. Optimizing
that process brings significant TTI improvements as I/O operations
on mobile devices tend to increase the overall latency.
NO JIT
The majority of modern browser engines use just-in-time (JIT)
compilers. It means that the code is translated and executed line-
by-line. However, the JIT compiler keeps track of warm code seg-
ments (the ones that appear a few times) and hot code segments
(the ones that run many times). These frequently occurring code
segments are then sent to a compiler that, depending on how
many times they appear in the program, compiles them to the ma-
chine code and, optionally, performs some optimizations.
On the other hand, JIT engines decrease the TTI as they need
time to parse the bundle and execute it in time. They also need
time to “warm up”. Namely, they have to run the code a couple of
times to detect the common patterns and begin to optimize them.
project.ext.react = [
entryFile: ''index.js'',
enableHermes: true
]
143
Optimize your application startup time with Hermes The Ultimate Guide to React Native Optimization
use_react_native!(
:path => config[:reactNativePath],
# to enable hermes on iOS, change `false` to `true` and
then install pods
:hermes_enabled => true
)
In both cases, whenever you switch the Hermes flag, make sure
to rebuild the project according to instructions provided in the na-
tive files. Once your project is rebuilt, you can now enjoy a faster
app boot time and likely smaller app size.
Apart from that, you can also look into other significant improve-
ments shipped by the Meta team. To do so, get familiar with
their write-up on React Native performance. It is often a game of
gradual improvements that make all the difference when applied
at once. The React Native core team has created a visual report
on benchmarking between stock RN and Hermes-enabled RN:
see here.
144
Optimize your application startup time with Hermes The Ultimate Guide to React Native Optimization
145
The Ultimate Guide to React Native Optimization
PART 2 | CHAPTER 5
Should you really care about app size in the era of super-fast
mobile internet and WiFi access everywhere? Why does a bun-
dle size grow so rapidly? We will answer those questions in this
section. But first, let’s have a look at what a typical React Native
bundle is made of.
147
Optimize your Android application’s size with these Gradle settings The Ultimate Guide to React Native Optimization
If you are not using them effectively, especially when your appli-
cation grows, you are unnecessarily increasing the overall size
of your application in bytes. That can have a negative impact on
the experience of your end users. We discuss it in the next section.
Right now, there are still markets where every megabyte of traf-
fic has its price. In those regions, the application’s size directly
impacts the conversion, and the installation/ cancellation ratio
increases along with the app size.
Source: https://segment.com/blog/mobile-app-size-effect-on-downloads/
148
Optimize your Android application’s size with these Gradle settings The Ultimate Guide to React Native Optimization
Now, let’s move to the last factor worth mentioning in this con-
text – device storage.
149
Optimize your Android application’s size with these Gradle settings The Ultimate Guide to React Native Optimization
The main advantage of the Android App Bundle over builds for
multiple architectures per CPU is the ease of delivery. After all,
you have to ship only one artifact and Dynamic Delivery will do all
the magic for you. It also gives you more flexibility on supported
platforms.
150
Optimize your Android application’s size with these Gradle settings The Ultimate Guide to React Native Optimization
You don’t have to worry about which CPU architecture your end
user’s device has. The average size reduction for an app is around
35%, but in some cases, it can be even cut in half, according to
the Android team.
Source: https://medium.com/google-developer-experts/
exploring-the-android-app-bundle-ca16846fa3d7
151
Optimize your Android application’s size with these Gradle settings The Ultimate Guide to React Native Optimization
By striving for a smaller APK size, you will do your best to reduce
the download cancellation rate. Also, your customers will benefit
from a shorter Time To Interactive and be more inclined to use
the app more often.
Finally, you will demonstrate that you care about every user, not
only those with top-notch devices and fast internet connections.
The bigger your platform gets, the more important it is to support
those minor groups, as every percent of users translates into
hundreds of thousands of actual users. If you’d like to learn more
about optimizing Android, check the Android Profiling chapter.
152
The Ultimate Guide to React Native Optimization
PART 2 | CHAPTER 6
EXPERIMENT WITH
THE NEW ARCHITECTURE
OF REACT NATIVE
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
The bridge is still working fine for most use cases. However, when
we start to send a lot of data over the bridge, it may become a bot-
tleneck for our app. This problem can be seen when rendering a lot
of components in a long list. In the case when the user scrolls fast,
there will be a blank space caused by the communication between
the JS and native sides being asynchronous. Essentially what
happens is that we are having a “traffic jam” on our bridge with
objects waiting to be serialized. The same issue with the bridge
being “overloaded” can be seen in native modules sending a lot
of data back and forth.
154
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
Codegen and JSI are two new tools improving developer experi-
ence. They are essential to understand how the new architecture
works.
Codegen
155
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
JSI
JSI is the foundation of the New Architecture, a C++ API for in-
teracting with any JS engine. In contrast to the bridge which was
asynchronous, JSI is synchronous which allows for invoking na-
tive functions faster. It lets JavaScript to hold references to C++
host objects and invoke methods directly on them. This removes
the major overhead of asynchronous communication between JS
and native by serializing objects using the bridge.
Fabric
TurboModules
Bridgeless mode
156
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
Performance
Due to the synchronous nature of the new architecture, while com-
municating with the native side, there will be some performance
improvements. The app's startup time will be significantly reduced
as every native module will be lazily-loaded. Once the bridgeless
mode will be available it will also remove the overhead of loading
the bridge at startup. However, not every scenario proves this, in
some of the benchmarks architecture performance is worse.
157
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
Meta's goal was not to make new architecture X times faster than
the old one. Apart from removing major bottlenecks they wanted
to create a new solid foundation which would allow for new ca-
pabilities that could not be developed using previous architecture.
Migration of the Facebook app took over a year and they haven't
noticed any significant performance improvements nor regres-
sions that are perceivable by the end user. However, this doesn't
mean that performance improvements won't come in the future.
Now that they reworked internals they have a great foundation
to build upon.
In this case new architecture proves that it's more efficient than
the old one. Using on average less CPU but more RAM.
158
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
The official response from the React Native team is that their in-
ternal benchmarks while rolling out the New Architecture to us-
ers was neutral across all React Native surfaces in the Facebook
app on both Android and iOS. As stated by Samuel Susla in
this discussion thread, “In the last years, we conducted dozens of
tests in production on millions of devices to assure performance
was neutral.”
FUTURE READINESS
New Architecture allows your app to leverage Concurrent React
features. Which improves UI responsiveness, provides Suspense
for data fetching to handle complex UI loading schemes, and en-
sures your app is ready for any further React innovations that will
be built on top of its new concurrent engine introduced in React 18.
159
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
Once you click on the button, the Value text UI will be updated but
the UI for the container will still remain green until the transition
is completed and the color will change to red due to the new UI
being rendered. That's the magic of React's concurrent rendering.
return (
<View>
<Text>Non urgent update value: {isPending ? 'PENDING' :
value}</Text>
<View style={[styles.container, backgroundStyle]}>
{dummyData.map((_, index) => (
<View key={index} style={styles.item} />
))}
</View>
</View>
);
};
160
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
Next, we see that React shows the old UI (viewed in green) for
the UI that depends on the nonurgent updates, which means
the updates that are wrapped in startTransition . We also
see a Pending text displayed, this is a way for React18 to tell us
that the new UI depending on this state is not yet ready. Once it's
ready, React flushes it and we don't see the Pending text anymore,
and the view color changes to red.
161
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
162
Experiment with the New Architecture of React Native The Ultimate Guide to React Native Optimization
The React Native team has dedicated capacity to help the com-
munity solve their app and library problems regarding new ar-
chitecture adoption in close cooperation. Although it's not stable
yet, it's worth trying out in your app today. Especially considering
that since React Native v0.72 the Interop Layer exists which al-
lows running most of the old architecture components with apps
that enabled new architecture.
163
The Ultimate Guide to React Native Optimization
PART 3
The question is: is your application ready for that? Does your de-
velopment pipeline accelerate the development and shipping fea-
tures with React Native?
Most of the time, you would like the answer to be simply yes. But
in reality, it gets complicated.
PART 3 | CHAPTER 1
166
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
Such scenarios may seem pretty rare, but they happen. Then, your
team may become so afraid of having another regression and
crash that it will lose its velocity and confidence.
Most of the mobile apps out there don't need a full test coverage
of the code they write.
The exceptions are situations in which the client requires full cov-
erage because of the government regulations they must abide by.
But in such cases, you're probably already aware of the problem.
167
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
It's crucial for you to focus your time on testing the right thing.
Learning to identify business-critical features and capabilities is
usually more important than writing a test itself. After all, you want
to boost confidence in your code, not write a test for the sake of
it. Once you do that, all you need to do is decide on how to run it.
You have quite a few options to choose from.
168
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
JAVASCRIPT TESTING
Writing tests for utility functions should be pretty straightforward.
To do so, you can use your favorite test runner. The most popular
and recommended one within the React Native community is Jest.
We'll also be referring to it in the following sections.
169
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
return (
<ScrollView>
{questions.map((q, index) => {
return (
<View key={q}>
<Text>{q}</Text>
<TextInput
accessibilityLabel=''answer input''
onChangeText={(text) => {
setData((state) => ({
...state,
[index + 1]: { q, a: text },
}));
}}
/>
</View>
);
})}
<TouchableOpacity onPress={() => onSubmit(data)}>
<Text>Submit</Text>
</TouchableOpacity>
</ScrollView>
);
};
170
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
without actually dealing with native APIs. Some people may find it
intimidating and hard to work with because of the low-level API.
That's why the community around React Native came out with
helper libraries, such as React Native Testing Library, providing
us with a good set of helpers to productively write your high-qual-
ity tests.
A great thing about this library is that its API forces you to avoid
testing the implementation details of your components, making it
more resilient to internal refactors.
render(<QuestionsBoard questions={allQuestions}
onSubmit={mockFn} />);
fireEvent.changeText(answerInputs[0], 'a1');
fireEvent.changeText(answerInputs[1], 'a2');
fireEvent.press(screen.getByText('Submit'));
expect(mockFn).toBeCalledWith({
1: { q: 'q1', a: 'a1' },
2: { q: 'q2', a: 'a2' },
});
});
171
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
They may help you and your team quickly add coverage to
the project. And at the same time, snapshots make adding
low-quality and hard-to – maintain tests too easy. Using helper
tools like eslint-plugin-jest with its no-large-snapshots option, or
snapshot-diff with its component snapshot comparison feature for
focused assertions, is a must-have for any codebase that lever-
ages this testing technique.
E2E TESTS
The cherry on top of our testing pyramid is a suite of end-to-end
tests. It's good to start with a so-called “smoke test” – a test en-
suring that your app doesn't crash on the first run. It's crucial to
have a test like this, as it will help you avoid sending a faulty app
to your users. Once you're done with the basics, you should use
your E2E testing framework of choice to cover the most important
functionalities of your apps.
172
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
Also, they are more likely to fail because of the issues related
to e.g. networking, file system operations or storage or memory
shortage. What's more, they provide you with little information
on why they do it. This test's quality (not only the E2E ones) is
called “flakiness” and should be avoided at all cost, as it lowers
your confidence in the test suite. That's why it's so important to
divide testing assertions into smaller groups, so it's easier to de-
bug what went wrong.
For the purpose of this section, we'll be looking at Detox – the most
popular E2E test runner within the React Native community.
Before going any further, you have to install Detox. This process
requires you to take some additional “native steps” before you're
ready to run your first suite. Follow the official documentation as
the steps are likely to change in the future.
This quick snippet shown above would ensure that the first ques-
tion is displayed.
await element(by.text(allQuestions[0])).toBeVisible();
});
173
Run tests for key pieces of your app The Ultimate Guide to React Native Optimization
There are various matchers and expectations that can help you
build your test suite the way you want to.
174
The Ultimate Guide to React Native Optimization
PART 3 | CHAPTER 2
HAVE A WORKING
CONTINUOUS INTEGRATION
(CI) IN PLACE
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
For better context, let’s take a look at the early days of the devel-
opment process. When you’re starting out, your focus is on ship-
ping the first iteration (MVP) as fast as possible. Because of that,
you may overlook the importance of the architecture itself. When
you’re done with the changes, you submit them to the repository,
letting other members of your team know that the feature is ready
to be reviewed.
An example of a workflow on Github, where changes are proposed in the form of a PR.
176
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
177
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
Using a CI service, you not only test your code but also build a new
version of the documentation for your project, build your app, and
distribute it among testers or releases. This technique is called
Continuous Deployment and focuses on the automation of releas-
es. It has been covered in more depth in this section.
178
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
CircleCI
version: 2.1
jobs:
android:
working_directory: ~/CI-CD
docker:
- image: reactnativecommunity/react-native-android
steps:
- checkout
- attach_workspace:
at: ~/CI-CD
- run: npm i -g envinfo && envinfo
- run: npm install
- run: cd android && chmod +x gradlew && ./gradlew
assembleRelease
workflows:
build_and_test:
jobs:
- android
Example of .circleci/config.yml
179
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
For example, you may want to use a Node container if you need
to run only your React unit tests. As a result, the container will
be smaller, have fewer dependencies, and will install faster. If
you want to build a React Native application in the cloud, you
may choose a different container, e.g. with Android NDK/SDK or
the one that uses OS X to build Apple platforms.
You can also modify the jobs execution schedule by adding fil-
ters, so, for instance, a deploy job will only run if the changes in
the code refer to the main branch.
You can define many workflows for different purposes, e.g. one
for tests that would run once a PR is opened, and the other to de-
ploy the new version of the app. This is what React Native does
to automatically release its new versions every once in a while.
180
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
EAS is a set of deeply integrated cloud services for Expo and React
Native apps, built and maintained by the team behind Expo. The
three most popular services it includes are:
• EAS Build: a cloud service that helps you build React Native
app bundles
• EAS Submit: a cloud service that lets you to upload your built
app bundles directly to TestFlight on the Apple App Store, and
your preferred track on the Android Google Play Store
• EAS Update: a service to deliver over the air (OTA) updates to
React Native apps
Another benefit of using EAS for building your React Native apps
is that you get build caching for your JavaScript, Android and iOS
dependencies out of the box with no configuration needed.
To set up EAS Build in an existing React Native app, you’ll first want
to install the EAS cli:
npm i -g eas-cli
181
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
eas login
To create app builds that run on real devices, you will need to con-
figure build signing: this means generating keystores for Android
and Provisioning Profiles and Certificates for iOS.
One of the perks of using EAS is that it comes with a cli tool
that can automatically create and update all of your build creden-
tials for you. The CLI prompts you to do this when you configure
your first build, or you can also manage the credentials without
triggering a build by running the eas credentials cli command.
After installing the CLI and logging in, run the following in the root
directory of your project:
eas build:configure
This will add the eas.json file to the root directory of your project:
{
''cli'': {
''version'': ''>= 6.0.0''
},
''build'': {
''development'': {
''developmentClient'': true,
''distribution'': ''internal''
},
''preview'': {
''distribution'': ''internal''
},
''production'': {}
},
''submit'': {
''production'': {}
}
}
182
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
''development:simulator'': {
''ios'': {
''simulator'': true
},
''developmentClient'': true,
''distribution'': ''internal''
}
183
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
To use a different build profile, for example the preview build, you
can run the same command with --profile preview :
Once the build is complete, the CLI will print out the URL for
the build, and any member of your team can download the app
to their device.
184
Have a working Continuous Integration (CI) in place The Ultimate Guide to React Native Optimization
While EAS is primarily used for building and submitting your na-
tive apps to the stores, it does also support running E2E tests as
part of your workflow.
GitHub UI reporting the status of CircleCI jobs, an example taken from React Native repository
185
The Ultimate Guide to React Native Optimization
PART 3 | CHAPTER 3
187
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
188
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
Again, you can either upload the files to the desired service (e.g.
App Store) manually or use a tool that will take care of that for
you. For the same reasons as before, we prefer to use an existing
solution – in this case, AppCenter by Microsoft.
For the purpose of this section, we will use Fastlane and AppCenter
in CircleCI pipelines to fully automate the process of app delivery
to the final users. Then, we will dive into the EAS Submit.
189
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
Next, you have to run the init command within the React Native
project. We will run the fastlane command twice from each
native folder. This is because React Native is actually two sepa-
rate apps at a low level.
Our lane uses the gradle action to first clean the build fold-
er, and then assemble the APK with signature based on passed
params.
190
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
default_platform(:android)
platform :android do
lane :build do |options|
if (ENV[''ANDROID_KEYSTORE_PASSWORD''] && ENV[''ANDROID_
KEY_PASSWORD''])
properties = {
''RELEASE_STORE_PASSWORD'' => ENV[''ANDROID_KEYSTORE_
PASSWORD'']
''RELEASE_KEY_PASSWORD'' => ENV[''ANDROID_KEY_
PASSWORD'']
}
end
gradle(
task: ''clean'',
project_dir: project_dir,
properties: properties,
print_command: false
)
gradle(
task: ''assemble'',
build_type: ''Release'',
project_dir: project_dir,
properties: properties,
print_command: false
)
end
end
191
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
You can start with the match action. It helps in managing and dis-
tributing iOS certificates and provisioning profiles among your
team members. You can read about the idea behind match in
the codesigning.
Note: Before you move any further, make sure that your
init match for your project. It will generate the required
certificates and store them in a central repository where
your team and other automation tools can fetch them.
Another action that you could use apart from match is gym .
Gym is similar to the Gradle action in a way that it actually per-
forms the build of your application. To do so, it uses the previously
fetched certificates and signs settings from match .
default_platform(:ios)
before_all do
if is_ci? && FastlaneCore::Helper.mac?
setup_circle_ci
end
end
192
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
platform :ios do
lane :build do |options|
match(
type: options[:type],
readonly: true,
app_identifier: ios_app_id,
)
cocoapods(podfile: ios_directory)
gym(
configuration: ''Release'',
scheme: ios_app_scheme,
export_method: ''ad-hoc'',
workspace: ios_workspace_path,
output_directory: ios_output_dir,
clean: true,
xcargs: ''-UseModernBuildSystem=NO''
)
end
end
You should be able to run lane build by running the same com-
mand as for Android:
Once you are done with configuring your projects, you are ready
to proceed with writing the lane that will package the produced
binaries and upload them to the App Center.
193
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
lane :deploy do
build
appcenter_upload(
api_token: ENV[''APPCENTER_TOKEN''],
owner_name: ''ORGANIZATION_OR_USER_NAME'',
owner_type: ''organization'', # ''user'' |
''organization''
app_name: ''YOUR_APP_NAME'',
file: ''#{ios_output_dir}/YOUR_WORKSPACE.ipa'',
notify_testers: true
)
end
That’s it! Now it is time to deploy the app by executing deploy lane
from your local machine.
194
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
version: 2.1
jobs:
deploy_ios:
macos:
xcode: 14.2.0
working_directory: ~/CI-CD
steps:
- checkout
- attach_workspace:
at: ~/CI-CD
- run: npm install
- run: bundle install
- run: cd ios && bundle exec fastlane deploy
workflows:
deploy:
jobs:
- deploy_ios
Pipeline for Android will look quite similar. The main differ-
ence would be the executor. Instead of a macOS one, a docker
react-native-android Docker image should be used.
EAS Submit
195
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
In order to use EAS Submit, you will first need to run eas build
to create the production .ipa for Apple and .aab for Android.
The EAS Build section in the previous chapter explains how to set
this up.
Once you have your build .ipa either on your local machine or
on EAS, open your terminal and run eas submit . The cli will ask
you to either choose a build from EAS or from your local machine,
you’ll be prompted to log into your Apple Developer account, and
the build will be uploaded to App Store Connect. It usually takes
a couple of minutes for it to finish processing and become avail-
able on App Store Connect.
Alternatively you can build and submit your app in one command
with
Using eas submit to upload your app will not make it immedi-
ately available on the Apple App Store. It is not possible to upload
to the App Store directly. Instead, eas submit will upload your
app to TestFlight from which you can choose to either publish it
to a test group on TestFlight, or create a release and submit it for
App Store review. Only after the app has passed review can it be
made available to users on the App Store.
196
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
First you will need to create your Android app on the Google Play
console and upload the first build manually. For this, you can
use eas build to create the build, download it from EAS and
drag and drop the .aab file to the app upload section on Google
Play Console.
During this process, you’ll have to fill in all the metadata about
your app including adding app screenshots, marketing descrip-
tions, terms and conditions and security and privacy declarations.
If you open Dashboard on your Google Play Console, make sure
all the items under ''Finish setting up your app'' are checked off.
Then open ''Publishing Overview'' and ensure all changes have
been submitted for approval and approved.
{
''submit'': {
''production'': {
''android'': {
''serviceAccountPath'': ''../path/to/api-xyz.json'',
''track'':''internal''
}
}
}
}
197
Don’t be afraid to ship fast with Continuous Deployment The Ultimate Guide to React Native Optimization
Google Play builds are uploaded to specific test tracks with ''in-
ternal'' being the lowest. You can upload to a different test track
or manually promote the release up from Google Play as it passes
each stage of testing.
198
The Ultimate Guide to React Native Optimization
PART 3 | CHAPTER 4
200
Ship OTA (Over-The-Air) when in an emergency The Ultimate Guide to React Native Optimization
Even though the review times have gotten much better over
the years, it is still a good escape hatch to be able to immediate-
ly recover from an error that slipped through the testing pipeline
and into production.
These are the popular ways to implement OTA into your app:
• CodePush: A service that is part of Microsoft's App Center
suite.
• EAS Update: A service that is created by Expo and is part of
EAS suite.
APP CENTER/CODEPUSH
Configuring the native side
201
Ship OTA (Over-The-Air) when in an emergency The Ultimate Guide to React Native Optimization
That's it! If you have performed all the changes on the native side,
your application is now OTA-ready.
For more advanced use cases, you can also change the default
settings on when to check for updates and when to download
and apply them. For example, you can force CodePush to check
for updates every time the app is brought back to the foreground
and install updates on the next resume.
202
Ship OTA (Over-The-Air) when in an emergency The Ultimate Guide to React Native Optimization
appcenter login
Once these steps are complete, all users running your app will re-
ceive the update using the experience you configured in the pre-
vious section.
That will give you the ownerName and appName that you're look-
ing for. As said before, you can either do this via UI by visiting App
Center, or by using the App Center CLI.
EAS Update
EAS Update is an EAS service for delivering Over the Air Updates.
It provides first-class support for instant updates in React Native
applications and is especially user-friendly if you're already using
Expo.
It serves updates from the edge with a global CDN and uses mod-
ern networking protocols like HTTP/3 for clients that support them.
Furthermore, it implements the Expo Updates protocol, which is
an open standard specification for instant updates.
203
Ship OTA (Over-The-Air) when in an emergency The Ultimate Guide to React Native Optimization
use EAS Update in your project, you'll need to install the eas-cli
To
package and log in to your Expo account using eas login .
Note: To get EAS Update working in your project with the bare
React Native workflow, you need to also set up Expo in
your project. See the guide to make that work correctly.
eas update:configure
eas build:configure
204
Ship OTA (Over-The-Air) when in an emergency The Ultimate Guide to React Native Optimization
Creating a build
Create a build of your app using EAS Build or another tool of your
choice. The new build will include the expo-updates native
module, which will be responsible for downloading and launching
your updates.
Creating an update
After installing the new build on your device, you're ready to send
an update to it! Make a small, visible change to the JS of the app.
You can also confirm this change by running the development
server with npx expo start locally on our machine.
This command creates an update. You can view this in our EAS
project's dashboard:
205
Ship OTA (Over-The-Air) when in an emergency The Ultimate Guide to React Native Optimization
Running an update
After this step is completed, all users running our app will receive
an update with the changes. By default, expo-updates checks
for the updates in the background when the app launches. When
testing with an internal distribution, to see the update in the app
you'll need to force close and reopen the app up to two times to
see the changes.
For example, it may happen that your backend will stop working
and it causes a crash at startup. It may be a mishandled error – you
206
Ship OTA (Over-The-Air) when in an emergency The Ultimate Guide to React Native Optimization
You can fix the problem by displaying a fallback message and in-
forming users about the problem. While the development will take
you around one hour, the actual update and review process can
take hours if not days. With OTA updates set up, you can react to
this in minutes without risking the bad UX that will affect the ma-
jority of users.
207
The Ultimate Guide to React Native Optimization
PART 3 | CHAPTER 5
209
Make your app consistently fast The Ultimate Guide to React Native Optimization
One of the most effective ways of doing that is using the DMAIC
methodology. It's very data-driven and well-structured and can
be used to improve React Native apps. The acronym stands for
Define, Measure, Analyze, Improve, and Control. Let's see how we
can apply each phase in our apps.
Define
Measure
210
Make your app consistently fast The Ultimate Guide to React Native Optimization
{
id: ''Component'',
phase: ''mount'',
actualDuration: 1.3352311453,
baseDuration: 0.95232323318,
...
}
211
Make your app consistently fast The Ultimate Guide to React Native Optimization
https://shopify.github.io/react-native-performance/docs/guides/flipper-react-native-performance
212
Make your app consistently fast The Ultimate Guide to React Native Optimization
Analyze
The goal of this phase is to find the root cause of our problem. It's
a good idea to start with a list of things that could potentially cause
the problem. A little brainstorming with a team is really helpful here.
Finally, it's time to test the hypothesis. For example, if the main
problem is low FPS, and the potential major cause is related to
list rendering, we can think of some improvements in the area of
images in the list items. We need to design a test that will help us
accept or reject the hypothesis – it will probably be some kind of
proof of concept. Next, we interpret the results and decide if it
was improved or not. Then we make a final decision.
213
Make your app consistently fast The Ultimate Guide to React Native Optimization
Improve
Now we know what our goal is and how we want to achieve it, it's
time to make some improvements. This is the phase where opti-
mization techniques start to make sense.
After outlining the solutions, it's time to pick the best one.
Sometimes the solution that gives the best effects might be ex-
tremely costly, e.g. when it's necessary to make some architec-
tural changes.
It's then time to implement the solution. After that, it's required to
properly test it and we are done!
Control
The last step is the control phase. We need to make sure that ev-
erything works well now. The performance will degrade if it is not
under control. People tend to blame devices, the used technology,
or even users when it comes to bad performance. So what do we
need to do to keep our performance on a high level?
We need to make sure that we have a control plan. We can use some
of our work from the previous phases to make it. We should point
out focal points, some measurement characteristics, acceptable
ranges for indicators, and testing frequency. Additionally, it is
a good practice to write down some procedures and what to do
if we spot issues.
214
Make your app consistently fast The Ultimate Guide to React Native Optimization
It's a great addition to any app builders that want to have in-
sights on how the performance is distributed across all the devic-
es that install their apps. Based on real user data.
215
Make your app consistently fast The Ultimate Guide to React Native Optimization
The simplest test you can write would look something like this:
Code: Component.perf-test.tsx
return (
<View>
<Pressable accessibilityRole=''button''
onPress={handlePress}>
<Text>Action</Text>
</Pressable>
<Text>Count: {count}</Text>
216
Make your app consistently fast The Ultimate Guide to React Native Optimization
fireEvent.press(button);
await screen.findByText('Count: 1');
fireEvent.press(button);
await screen.findByText('Count: 2');
fireEvent.press(button);
fireEvent.press(button);
fireEvent.press(button);
await screen.findByText('Count: 5');
};
After running this command, we can start optimizing our code and
see how it affects the performance of our component. Normally, we
would keep the baseline measurement and wait for performance
217
Make your app consistently fast The Ultimate Guide to React Native Optimization
Once we're done, we can run Reassure a second time. Now with-
out the --baseline flag .
Now that Reassure has two test runs to compare – the current and
the baseline – it can prepare a performance comparison report. As
you can notice, thanks to applying memoization to the SlowList
component rendered by AsyncComponent , the render duration
went from 78.4 ms to 26.3 ms, which is roughly a 66% perfor-
mance improvement.
218
Make your app consistently fast The Ultimate Guide to React Native Optimization
When connected with Danger JS, Reassure can output this report
in the form of a GitHub comment, which helps catch the regres-
sions during code review.
You can discover more use cases and examples in the docs.
219
Make your app consistently fast The Ultimate Guide to React Native Optimization
220
The Ultimate Guide to React Native Optimization
PART 3 | CHAPTER 6
Xcode provides some basic tools to do the first report. You can
monitor the CPU, Memory, and Network.
222
Know how to profile iOS The Ultimate Guide to React Native Optimization
When the app is running and you are on the screen you want to
inspect, click on Debug View Hierarchy.
This will show your current UI in a 2D/3D model and the view tree.
This will help you to detect overlappings (you can’t see a compo-
nent) or if you want to flatten your component tree. Even though
RN does a view flattening it sometimes can’t do it with all of them,
so here we can do some optimization focusing on specific items.
Let’s say we have a TODO list app, and when the Add button is
pressed, it adds the new item to the list. However, it takes a cou-
ple of seconds to show up on the list because there is some logic
223
Know how to profile iOS The Ultimate Guide to React Native Optimization
before the item is added. Let’s go to our dev toolbox and pick up
our first instrument so we can confirm and measure the delay.
IOS INSTRUMENTS
Instruments is a debugging and profiling tool that comes pre-
packaged with xCode, and is literally a box of tools, each of them
serving a different purpose. You choose from a list of templates,
and you choose any of them depending on your goal: improving
performance or battery life or fixing a memory problem.
We are going to use Time Profiler. Let’s dive into it. With xCode
open, we go to Open Developer Tool – > Instruments. Then, scroll
down to find the Time Profiler tool.
When the app opens, start using it normally, or in this case, add
a new TODO item.
224
Know how to profile iOS The Ultimate Guide to React Native Optimization
After playing around and adding the new TODO item, we can see
there is a big blue rectangle, which means there is something
that is taking a lot of time to finish. Let’s take a look at the threads.
225
Know how to profile iOS The Ultimate Guide to React Native Optimization
We click Start so the profiler begins. We do the same flow and ac-
tions as before when profiling with Time Profiler. When we Stop,
we will see all the data collected.
By default the data will be sorted bottom-up with the heavy tasks
at the top. We can see that a function called doFib is taking ~14
sec to complete, it is a good start, let’s go into that function and
see what we can do. The fixes will vary depending on your code.
226
Know how to profile iOS The Ultimate Guide to React Native Optimization
Start profiling the app one more time using Hermes Debugger
(RN) – > Profiler.
Prewarming Mechanics
227
Know how to profile iOS The Ultimate Guide to React Native Optimization
With the advent of iOS 15, initializers and other preparatory steps
can be executed hours ahead of the actual app startup. Developers
must, therefore, account for the interval between the commence-
ment of the process in the pre-main initializer and the subsequent
post-main period. Otherwise, they may notice a lot of very high
numbers in their monitoring tools.
if ProcessInfo.processInfo.environment[''ActivePrewarm''] ==
''1'' {
// Handle prewarmed app launch scenario
} else {
// Handle regular app launch scenario
}
228
Know how to profile iOS The Ultimate Guide to React Native Optimization
229
The Ultimate Guide to React Native Optimization
PART 3 | CHAPTER 7
KNOW HOW TO
PROFILE ANDROID
Know how to profile Android The Ultimate Guide to React Native Optimization
231
Know how to profile Android The Ultimate Guide to React Native Optimization
If you have not installed Android Studio yet, you can install it us-
ing this link.
To open the Profiler, choose View > Tool Windows > Profiler from
the Android Studio menu bar:
232
Know how to profile Android The Ultimate Guide to React Native Optimization
• Turn off development mode. You must be sure that the app
uses a JS bundle instead of the metro server, which provides
that bundle. To turn it off, please share your device, click on
Settings and find JS Dev Mode:
After that, go to the Profiler tab and add a new profiler session:
Wait for the session to attach to your app and start performing
actions that could cause some performance issues, like swiping,
scrolling, navigating, etc. Once you’re done, you should see some
metrics like these:
Each greenfield React Native app has only one Android Activity. If
your app has more than one, it’s most likely a brownfield one. Read
more about the brownfield approach here. In the above example,
233
Know how to profile Android The Ultimate Guide to React Native Optimization
234
Know how to profile Android The Ultimate Guide to React Native Optimization
Let’s imagine you would like to pick the best list or scroll view com-
ponent for your React Native app, which has the best performance
on a lower-end device. You noticed the current solutions could be
235
Know how to profile Android The Ultimate Guide to React Native Optimization
Here you can see the mqt_js thread is used almost all the time
and does some heavy computation because your computations
are done on the JS side. You can start thinking about how to im-
prove it. There are multiple options to check:
• Replace the bridge with JSI in terms of communication – do
tests if JSI is faster than the bridge.
• Move some part of the code to the native side – on the native
side you have more control over threads execution and can
schedule some work to not block the JS or UI thread.
• Use a different native component – replace the native scroll
view with your custom solution.
• Use shadow nodes – do some expensive calculation with C++
and pass it to the native side.
You can try out all of those solutions and compare the effect be-
tween each other. The profiler will provide you with a metric and
based on that you can make a decision about which approach fits
best to your particular problem.
SYSTEM TRACING
Using the Android Studio CPU Profiler, we can also make a sys-
tem tracing. We can check when the appropriate function has
been called. We can triage all threads and see which function is
the costliest which affects the UX. To enable system tracing, click
on the CPU section and select System Trace Recording
236
Know how to profile Android The Ultimate Guide to React Native Optimization
After some interaction, you should be able to see all the threads
with details:
237
Know how to profile Android The Ultimate Guide to React Native Optimization
You can also save your data by clicking the Save Button:
You can find more about threading models in the New Architecture
chapter.
238
Know how to profile Android The Ultimate Guide to React Native Optimization
You can also automate your experiments with e2e tests and gen-
erate reports locally or on a CI. Those reports can be used to com-
pare solutions with each other.
239
The Ultimate Guide to React Native Optimization
THANK YOU
We hope that you found the aforementioned best practices for
React Native optimization useful and that they will make your
work easier. We did our best to make this guide comprehensive
and describe both the technical and business aspects of the opti-
mization process.
AUTHORS
MICHAŁ PIERZCHAŁA
As Head of Technology at Callstack, he is passionate about build-
ing mobile and web experiences, high-quality JS tooling, and Open
Source. Core Jest and React Native community contributor. Space
exploration enthusiast.
twitter.com/thymikee
github.com/thymikee
JAKUB BUJKO
With multiple years of delving deep into react.js development
in his pocket, Kuba went on to master mobile development.
Passionate about edge technologies, clean and minimalistic code,
and charting the paths for the future of React and React Native
development.
twitter.com/f3ng liu
github.com/Xiltyn
MACIEJ JASTRZĘBSKI
React & React Native developer with multiple years of experience
building native iOS and Android apps. Passionate about building
robust and delightful apps along with writing well-architected and
readable code. Loves learning new things. He likes to travel in his
free time, hike in the mountains, and take photographs.
twitter.com/mdj_dev
github.com/mdjastrzebski
241
Thank you The Ultimate Guide to React Native Optimization
PIOTR TROCKI
Software developer who started his journey from mobile apps.
Now Piotr is focused on mastering both Native (Android, iOS) and
React Native technologies in brownfield applications. When not
coding, he spends his free time on the dance floor.
twitter.com/Tr0zZe
github.com/troZee
JAKUB BINDA
A dedicated software developer who pays a lot of attention to
the details in every task he does. Always committed and eager
to learn, Kuba likes to create things and dive into how they work.
A father of two and a husband to the woman of his life. Those two
roles motivate him the most and give him the strength to move
mountains.
github.com/jbinda
SZYMON RYBCZAK
Szymon is a 17-year-old React Native Developer with three years
of experience and currently doing mobile app development at
Callstack. In his free time, he likes to discover new and interest-
ing technologies.
github.com/szymonrybczak
twitter.com/SzymonRybczak
242
Thank you The Ultimate Guide to React Native Optimization
HUR ALI
TypeScript enthusiast mastering the React-Native and Native realm.
He feels best in diving deep with mobile tech, making proof-of-
concept projects, and experimenting with new technologies. In his
free time, he enjoys playing FIFA and contribution to OSS.
twitter.com/hurali97
github.com/hurali97
OSKAR KWAŚNIEWSKI
React Native Developer at Callstack. Currently, he’s strengthen-
ing his knowledge of native development and making some OSS
contributions. During his free time, he enjoys riding a bike, going
to the gym, and playing video games.
github.com/okwasniewski
twitter.com/o_kwasniewski
TOMASZ MISIUKIEWICZ
React Native Developer at Callstack with a strong background in
web development. Big fan of keeping the code clean and simple.
Loves to learn new stuff and enhance his programming skillset
every day.
github.com/TMisiukiewicz
EDUARDO GRACIANO
Senior mobile developer at Callstack. Hacking almost all kinds of
mobile tech and always looking forward to making readable and
maintainable code without messing up everything.
github.com/gedu
twitter.com/teddydroid07
243
Thank you The Ultimate Guide to React Native Optimization
ANDREW ANDILEVKO
React Native developer with a background in Android develop-
ment. He likes complex tasks to constantly expand his expertise
and knowledge. He spends his free time with his wife and pug.
github.com/andrewworld
JAMES IDE
I work on Expo, which I co-founded with Charlie Cheever when we
wanted to make it easier to make and use universal mobile apps
that run everywhere.
https://github.com/ide
https://twitter.com/JI
GRZEGORZ KRUK
Senior Frontend Developer with years of experience in building
mobile and web solutions in multiple frameworks and libraries.
After mastering web development, he’s become passionate about
building beautiful and neat mobile solutions in React Native.
https://github.com/grzegorzkruk
KANSTANTSIN KIYKO
JavaScript expert with experience in mobile and web apps devel-
opment. Has a can-do attitude, loves to solve complex problems
and automate things.
https://twitter.com/xfozzyx
https://github.com/sneakyechidna
244
Thank you The Ultimate Guide to React Native Optimization
JACEK PACIOREK
React Native Developer at Callstack with full stack development
background. Loves to explore boundaries between software and
hardware and tinkering with IoT devices. Likes a good challenge
and gets stuff done. Besides that, he is obsessed with cars – loves
driving them, fixing them up and sharing his passion with others.
Also tries to stay active by skiing and sailing
https://github.com/booua
CATALIN MIRRON
I like to give life to UIs through animation and I am doing so for more
than a decade now. I am the creator of AnimateReactNative.com,
the most comprehensive collection of React Native Animations.
https://twitter.com/mironcatalin
https://github.com/catalinmiron
245
The Ultimate Guide to React Native Optimization
ABOUT CALLSTACK
Callstack is the Total Software Engineering consultancy that de-
velops high-performing cross-platform apps set in the React
Universe. We work with global enterprise clients such as PwC,
Major League Soccer and AutoZone, and fast-growing startups
and SMEs like Evernote and Coinmine.