Over a year ago I wrote an article about a kind of hacky way to use react-native-firebase in Expo app. But things have changed significantly since then. In this article, I’m going to show you the new, much easier way to do this.This is also a getting started guide for new features that Expo has released recently.
TL;DR Scroll down, there’s a tutorial down below. 😉
A long time ago…
It was Expo SDK 36’s when I was trying to integrate native Firebase with an Expo app. It was a tough time because Expo barely supported custom native code. That is — I had to eject to bare workflow in order to be able to use any native library. And ejecting meant that I couldn’t use
expo build
anymore to build my app. Expo Client (now called Expo Go) didn’t support custom native code either.Since I didn’t want to opt out of Expo Go for development, I came up with the idea of using Expo-compatible Firebase Web (JavaScript) SDK in development and, at the same time, keeping ejected native projects with the more performant and fully featured native SDKs installed using react-native-firebase. I had a common JS codebase, which deduced in which environment it runs (Expo Go or bare RN app) and automatically switched to the correct Firebase backend.
My solution was working, but it was very cumbersome to maintain in further project stages. I had to take care of both bare and managed app separately. Things like permissions, app icon, or splash screen required separate steps for Android, iOS and managed app. Nightmare.
The new, right way!
Fortunately, Expo made huge progress! Thanks to the config plugin system, managed apps can be easily prebuilt to generate native projects, also with third-party libraries. Their new EAS Build service can build both managed and bare (!) apps. And recently released development clients allow you to build your own, customized Expo Go with any native code you wish! Impressive, isn’t it?
Ok, I’m hyped about that new wonderful Expo stuff, but what does it mean in the context of my Firebase integration? Let me explain briefly:
- I’m building my app using a semi-managed workflow. That is: I do everything like it was managed workflow, but I also have some JavaScript code, which calls native libraries like react-native-firebase.
- Instead of the Expo Go app, I’m using my custom development client with react-native-firebase bundled with it.
- Each time I need a native change, e.g. install a native library, I rebuild my Dev Client using either EAS Build or locally using
expo run:android
/expo run:ios
. You can read more about these commands in this blog post.
- If a native library requires custom configuration — e.g. adding some code or modifying Android manifest, that configuration is applied during prebuilding phase thanks to Config Plugins.
- A prebuild is actually very similar to eject, but its name no longer sounds like it’s a one-way ticket. It’s designed to be run multiple times, without losing the possibility to use Expo managed features.
The whole process has been explained more deeply in the blog series: Expo Managed Workflow in 2021 — part one, part two.
Cool, but… how to make it work? 🤔 A detailed step-by-step guide is here!
Ok, that was so much talking. Let’s dive straight into the actual solution:
First of all, ensure that you have the latest
expo-cli
installed. You can quickly do it with yarn global add expo-cli
.1. Start a project
If you have an existing managed Expo project, you can use it here, but please git commit first to make a checkpoint. I wouldn’t recommend using bare projects since prebuilding has undefined behavior on existing native projects! This doesn’t mean that you can’t use development clients in bare apps — you just have to install it manually.
Tip: When creating new project, you can run npx create-react-native-app -t with-dev-client to have a custom development client already set up. However, I decided to show more manual/universal solution, which should also work for existing projects.
I chose to start a new managed project and selected a blank (TypeScript) template:
Creating an app with expo init
2. Set up a development client
We want to develop our app with a custom development client. In order to do that, install a proper module:
expo install expo-dev-client
This package makes your debug native configuration build a custom development client. The release configuration remains unchanged; it builds a production app without the
expo-dev-client
code included, aside from a few header files.Since you’ll be building your app anyway, you can skip this step for now and come back later. If so, jump straight to Firebase installation.
When running commands from below for the first time, you’ll be asked for Android Application ID (package name) and iOS Bundle Identifier, unless you have provided them earlier in
app.json
.Try to build and start your dev client by running either
expo run:android
or expo run:ios
. Alternatively, you can also use EAS Build to do the job, but unless you created a project from with-dev-client
template, you’ll have to configure eas.json
yourself (you can use eas build:configure
to automate this).Note: expo run commands start Metro bundler by default unless --no-bundler flag is provided. To start the bundler without triggering the build process, use expo start --dev-client.
If everything goes well, after a short while you should see your custom development client running on an emulator.
A running Development Client
3. Install Firebase
Now you can install react-native-firebase JS libraries of your choice. In my app I want to use the Firestore module:
expo install @react-native-firebase/app @react-native-firebase/firestore
In the meantime, you need to open the Firebase Console and create Android and iOS apps for your project. You’ll be asked about Android App ID and iOS Bundle Identifier. You have to provide the same values that you typed in earlier.Please download the
google-services.json
and GoogleServices-Info.plist
— we’re going to need them in a moment.Are we ready? Almost. When you visit the react-native-firebase website, you can see that installation steps require native changes. This is where config plugins unleash their power. A plugin’s purpose is to apply native configuration changes each time when building with EAS Build or running
expo prebuild
/ expo run
, without bothering you with native stuff.Fortunately, React Native Firebase (since version 12.4.0) is shipped with some built-in plugins, which do all the necessary configuration. If the plugins weren’t shipped, you’d need to read the config plugins guide, but because they are, all you have to do is to add them to the
plugins
section in your app.json
and provide paths to Google-Services files. Your app.json
file should contain these lines:{ "expo": { "android": { "googleServicesFile": "./google-services.json" }, "ios": { "googleServicesFile": "./GoogleService-Info.plist" }, "plugins": [ "@react-native-firebase/app" ] } }
If, for some reason, you need to use older React Native Firebase version than 12.4.0, you can use the
with-rn-firebase
plugin, which has very similar functionality.The plugin idea doesn’t only apply to Firebase; it applies to any native library that requires additional configuration after linking. Since the config plugins API is pretty new, there aren’t yet many libraries that support config plugins. That’s a large area for open-source contributors. You are very welcome to help developing config plugins for your favourite libraries!
4. Run your app
Now, all that’s left to do is to build your app again (EAS Build /
expo run
) and you are free to start using Firebase!A quick example of Firestore usage in an Expo app
This way you have set up react-native-firebase in your own custom Expo Go… I mean development client 😅. Of course, the stuff described in this article is only the tip of an iceberg. There are many more awesome features, including a customizable developer menu. You may also want to share your custom dev client with your team/friends, or even automate it with the CI. You can learn more about all that stuff in Expo Docs.