Darshan Ponikar.

Let's understand Expo framework for React Native

Cover Image for Let's understand Expo framework for React Native
Darshan Ponikar
Darshan Ponikar

Introduction

Hi, my name is Darshan Ponikar. I have been building cross-platform applications in React Native since 2019. Almost a decade ago, building a fully native app using a language like JavaScript was like a miracle. I would like to thank all the grateful people in the community who have made this work. I can see the future of React Native has lots of potentials since the emergence of the new architecture. Although this talk is not about JSI, it's about how to decide whether you should use Expo or React Native.

Before we go ahead, let's refresh our knowledge a bit. React Native is a renderer very similar to React DOM, which renders native views. Expo is a framework for React Native to build production ready application.

TL;DR use Expo as much as possible

memes describe to use Expo meme

memes describe to use Expo meme

Meme credits to Tomasz Zawadzki

You were probably wondering how much confidence I have shown in the above statement without proving anything yet. Last week in the React Conf, while delivering the keynote, Nicola from Meta and one of the maintainers and core contributors in React Native said a very fine line that has stuck in my mind. I will elaborate here in this blog.

Nicola said while using React Native for building an app, "you either use an existing framework or create your own framework." and this statement makes a lot of sense if you are building a React Native app for almost a year.

screenshot of App.js 2024 conference, Nicola is talking about React Native frameworks

screenshot of App.js 2024 conference, Nicola is talking about React Native frameworks

By the way, React Native documentation recommends using Expo by default.

Cross-platform learning curve

I guess one of the reasons you have chosen to learn and build apps in React Native is (you can guess it) JavaScript. Any JavaScript developer with a decent level of knowledge can build a native app, but wait until you realize it’s half of the story, it has lots of twists. You encounter a library that requires adding native code, so now you are updating Gradle scripts, pod files, Java, and Objective C files. You can easily spend around 2 hours just setting up a library you want. Let's be honest, we've all been there, and at a certain point, we all wanted to quit React Native.

Expo understood this problem very early. Most of the engineers have a web background, and they probably don't want to spend much time understanding the native world. (which eventually you have to at some point)

Philosophy of Expo

I have a mental model to understand this better. Your JavaScript code has a business logic that is expected to change often. Your native binaries (APKs, IPAs) act as a container that is responsible for loading and executing your JavaScript code. In addition, it also communicates via (bridge old architecture) or JSI (new architecture).

Expo loads your JavaScript in a pre-configured app (Expo Go). So you don't have to spend time waiting for the native build to get done. This is how Expo started initially. You have seen often that in your Expo Go app it only loads your JavaScript code and doesn't run any native code because it's pre-configured.

It works as battery included, Expo has its own ecosystem which is also a part of the React Native ecosystem. Expo provides first-class support so you don't need to configure anything. You can just download any Expo library and start working on it.

Expo abstracts away the native complexity which doesn't worth time spending.

But what about the time when it hits its limitation? Expo has come a long way. When I first heard about Expo in 2019, I was a little skeptical about it because there was no way to access native code (iOS and Android) except you have to eject the app, which was literally a nightmare to do. You may end up with horrid errors. Well, that was the old story of Expo.

Expo dev server

2-3 years back, the Expo team announced Expo Dev Server and that was really a game changer. One of the pain points of Expo which I mentioned earlier was vendor locking. Eventually, you had to eject your app in order to add custom native code which is not a very safe option.

Expo Dev Server lets developers build native binaries beyond Expo Go app. In simple words, you can run a native build (Android and iOS) unlike Expo Go app. That also gives you a sort of control over native directories.

Expo Dev Server generates iOS and Android folders. Whenever you run a command like expo prebuild this generates Android and iOS directory for your project. These directories are fully managed by the Expo framework meaning whenever you run this command expo prebuild these directories will be generated.

So you cannot make any changes directly in any files which belong to either iOS and Android folders. You’re probably wondering then what is the use case of having Expo Dev Server? Well, it basically gives a way to add native code via plugins.

The working mechanism of the plugin is very similar to a Babel plugin but here it's specially built to make changes in your native code. You can specify plugins in app.json or app.js file in the plugin sections.

How plugins work?

Let's take a very simple example.

Let's say you want to update your compileSdkVersion which requires native changes. Expo has a plugin called expo-build-properties that lets you edit native configuration like this.

Add the plugin in the app.json file.

{ "expo": { "plugins": [ [ "expo-build-properties", { "android": { "compileSdkVersion": 31 } } ] ] } }

Notice how the plugin is an array, first for plugin name and second for passing different arguments.

That's it, no manually updating gradle files inside the Android folder. Once you make this change you have to run this expo prebuild command in order to reflect these changes inside the gradle file.

Once you run the command, first it creates a fresh folder and then it goes over the plugin array and makes changes in the native directory based on the plugin behavior.

Many libraries outside of the Expo ecosystem provide a plugin to add support. This way you are making sure not to break any native dependencies and have consistency inside native files.

You can also create your own plugin by following this document. You can edit files like MainActivity.java, AndroidManifest.xml, updating PodFile, etc.

This gives me confidence to never leave Expo behind because eventually you can create your own plugin to make something work.

But wait a minute? What if the plugin hits its limitation? What if I want to customize something which is beyond the capability of the plugin?

Beyond the Plugin limit

Plugins are a great way to make smaller changes and configure your native files but if it requires more than this then implementing something like Native modules makes more sense.

Expo has a solution for that too as Expo Modules. Think of Expo Modules as native modules but more powerful. It provides a wrapper around the native module and gives you full control to write native modules in languages like Swift and Kotlin. You can learn more about this. It has made building native modules a very seamless process that a person with very superficial knowledge can build a native module. Expo Modules provide first-class support for Expo apps but if you want to use it outside of Expo you have to set up Expo in the app first.

Till now we have talked about how powerful Expo and its ecosystem are but what about React Native CLI? Is it obsolete? Well, no solution is sufficient for all the problems you will face in your career.

We have not talked about eas-cli in this blog. It’s a powerful CLI tool built to manage the deployment of your mobile application. I want to share my understanding more on this which can be a mini blog itself. So I saved it for later.

While Expo is a really good choice for startups and mid-level companies where you want to build an app from scratch and the complexity of the application is moderate to high. Using Expo makes sense.

But working on brownfield apps that are highly dependent on native code, in this case, sticking to something like React Native CLI makes sense because A) you have to bring Expo first to bring Expo and its ecosystem support B) you also need to support backward native modules. While this can be one of the solutions that you can propose, measuring the complexity of the existing application is more complex than building an app from scratch.

Both React Native CLI and Expo are great and proven ways to build your application. The point is which one makes more sense for your use case.

My experience building a cross-platform app at Nintee

Although I have many things to share on different learning experiences I had while working at Nintee. It can be an entirely different blog post.

We as a team worked on multiple React Native projects, we started with React Native CLI but as we started exploring different product ideas, starting everything from the beginning with React Native CLI was slowing us down as a team. Introducing Expo helped us move faster. We were using development builds to use native libraries. This led us to focus only on building a production-ready application.

I am excited for the future of React Native with new things coming every year which seems really promising to build the next generation of applications.

Thanks for reading this blog. I hope this blog has given you some sense to pick your next tech stack.

In the future, I will write more around cross-platform mobile engineering. You can follow me on Twitter and ask any questions in DM.

We will meet again with a new article.