Topic 12: Firebase
📖 9 min read · 🎯 advanced · 🧭 Prerequisites: libraries-image-picker-react-elements, redux
Why this matters
Up until now, everything we've built lives only on the device — close the app, and the data is gone. That works for practice, but real apps need more. Users expect to sign in, pick up where they left off, and see their data on any phone. That's where Firebase comes in. It's Google's ready-made backend — authentication, cloud storage, real-time sync — all without you having to set up a single server. In this lesson, we wire our React Native app into Firebase so users can sign in and have their data live in the cloud.
What You'll Learn
- Create and configure a Firebase project for a React Native Android app
- Install and set up
@react-native-firebase/app,auth, andfirestorepackages - Initialize Firebase in a dedicated
firebase.jsmodule - Implement email/password sign-in and sign-out with Firebase Authentication
- Read and write user documents in Firestore after authentication
The Analogy
Think of Firebase as a fully staffed hotel concierge service for your app. You don't build the kitchen, the security desk, or the records room yourself — you just call the concierge and say "check in this guest" or "retrieve their luggage from storage." The concierge (Firebase) handles identity verification (Authentication), keeps a real-time ledger of guest preferences (Firestore), and scales to ten thousand guests without you hiring extra staff. Your React Native app is the hotel lobby: beautiful, interactive, and completely reliant on the back-of-house operation that Firebase provides invisibly.
Chapter 1: Setting Up Firebase
Before writing a single line of JavaScript, the Firebase project itself must exist and your Android app must be registered within it.
Step 1: Create a Firebase Project
- Go to the Firebase Console.
- Click Add project and follow the prompts to name and create your project.
- Once created, click Add app and select the Android icon.
Step 2: Register Your App
- Enter your app's package name (e.g.,
com.yourappname) and click Register app. - Download the
google-services.jsonfile that Firebase generates. - Place
google-services.jsoninside theandroid/app/directory of your React Native project.
Step 3: Add Firebase SDK to Your Android Project
Open android/build.gradle and add the Google Services classpath inside the buildscript dependencies block:
buildscript {
dependencies {
// Add this line
classpath 'com.google.gms:google-services:4.3.10'
}
}
Open android/app/build.gradle and apply the plugin at the very bottom of the file:
apply plugin: 'com.google.gms.google-services'
Still in android/app/build.gradle, add the Firebase SDK dependencies inside the dependencies block:
dependencies {
// Add these lines
implementation platform('com.google.firebase:firebase-bom:28.4.2')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-auth'
implementation 'com.google.firebase:firebase-firestore'
}
The Firebase BOM (Bill of Materials) at version 28.4.2 pins compatible versions for all Firebase libraries automatically — you don't need to track individual version numbers.
Chapter 2: Installing React Native Firebase Packages
The native Android SDK is wired up; now the JavaScript layer needs its counterparts. The @react-native-firebase suite wraps the native SDKs through auto-linking.
Step 1: Install the Core Package
npm install @react-native-firebase/app
This core package is required for every Firebase service. It bootstraps the connection using google-services.json — no manual initializeApp() call is needed when this file is present.
Step 2: Install Additional Firebase Modules
Install only the modules your app actually uses. For Authentication and Firestore:
npm install @react-native-firebase/auth @react-native-firebase/firestore
Each module is a separate package that adds native bindings for that Firebase service. If you later need Cloud Storage, Cloud Messaging, or Remote Config, you install their respective @react-native-firebase/* packages the same way.
Chapter 3: Configuring Firebase in React Native
Rather than importing Firebase ad-hoc across every file, create a single firebase.js module at the project root. This becomes the single source of truth for all Firebase service instances.
firebase.js:
import firebase from '@react-native-firebase/app';
import '@react-native-firebase/auth';
import '@react-native-firebase/firestore';
// Optional: manual config override (not required when google-services.json is present)
// const firebaseConfig = {
// apiKey: 'YOUR_API_KEY',
// authDomain: 'YOUR_AUTH_DOMAIN',
// projectId: 'YOUR_PROJECT_ID',
// storageBucket: 'YOUR_STORAGE_BUCKET',
// messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
// appId: 'YOUR_APP_ID',
// };
// if (firebase.apps.length === 0) {
// firebase.initializeApp(firebaseConfig);
// }
export { firebase };
Side-effect imports (import '@react-native-firebase/auth') register each service with the core firebase instance. The commented-out firebaseConfig block shows how to manually initialize Firebase — useful in environments where google-services.json is not committed to the repo (e.g., CI secrets). In standard development, the file alone is sufficient.
Chapter 4: Using Firebase Authentication
With Firebase configured, the class implemented email/password authentication — the most common starting point for user identity in mobile apps.
App.js (Authentication UI):
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
import { firebase } from './firebase';
export default function App() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [user, setUser] = useState(null);
const signIn = async () => {
try {
const userCredential = await firebase.auth().signInWithEmailAndPassword(email, password);
setUser(userCredential.user);
} catch (error) {
console.error(error);
}
};
const signOut = async () => {
try {
await firebase.auth().signOut();
setUser(null);
} catch (error) {
console.error(error);
}
};
return (
<View style={styles.container}>
{user ? (
<View>
<Text>Welcome, {user.email}</Text>
<Button title="Sign Out" onPress={signOut} />
</View>
) : (
<View>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Sign In" onPress={signIn} />
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
input: {
width: '100%',
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 12,
paddingHorizontal: 8,
},
});
Key points:
signInWithEmailAndPassword(email, password)returns aUserCredential; the.userproperty holds the authenticated user object including.uidand.email.signOut()clears the Firebase auth session; resetting localuserstate drives the UI back to the login form.secureTextEntryon the password field masks input — always include this for credential fields.
Chapter 5: Using Firestore
Authentication gives you an identity; Firestore gives you a place to store everything about that identity. The class updated the app to save and retrieve a user document on every sign-in.
Step 1: Export the Firestore Instance
Update firebase.js to export a firestore instance alongside the core firebase export:
firebase.js:
import firebase from '@react-native-firebase/app';
import '@react-native-firebase/auth';
import '@react-native-firebase/firestore';
export const firestore = firebase.firestore();
export { firebase };
Step 2: Integrate Firestore into Authentication Flow
Update App.js to read from (or create) a Firestore document whenever a user signs in successfully:
App.js:
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
import { firebase, firestore } from './firebase';
export default function App() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [user, setUser] = useState(null);
const [userData, setUserData] = useState({});
useEffect(() => {
if (user) {
const userDoc = firestore().collection('users').doc(user.uid);
userDoc.get().then((doc) => {
if (doc.exists) {
setUserData(doc.data());
} else {
userDoc.set({ email: user.email });
setUserData({ email: user.email });
}
});
}
}, [user]);
const signIn = async () => {
try {
const userCredential = await firebase.auth().signInWithEmailAndPassword(email, password);
setUser(userCredential.user);
} catch (error) {
console.error(error);
}
};
const signOut = async () => {
try {
await firebase.auth().signOut();
setUser(null);
setUserData({});
} catch (error) {
console.error(error);
}
};
return (
<View style={styles.container}>
{user ? (
<View>
<Text>Welcome, {userData.email}</Text>
<Button title="Sign Out" onPress={signOut} />
</View>
) : (
<View>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Sign In" onPress={signIn} />
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
input: {
width: '100%',
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 12,
paddingHorizontal: 8,
},
});
Firestore concepts at work here:
firestore().collection('users')— references theuserstop-level collection..doc(user.uid)— each user gets a document keyed by their Firebase Auth UID, making lookups O(1) and globally unique.doc.get()— fetches the document once (not a real-time listener).doc.existstells you whether the document was previously created.userDoc.set({ email: user.email })— creates the document on first login, bootstrapping the user record.- On sign-out, both
useranduserDataare reset to avoid stale data leaking into the next session.
sequenceDiagram
participant U as User
participant App as React Native App
participant Auth as Firebase Auth
participant FS as Firestore
U->>App: Enter email + password, tap Sign In
App->>Auth: signInWithEmailAndPassword()
Auth-->>App: UserCredential { user }
App->>App: setUser(userCredential.user)
App->>FS: collection('users').doc(uid).get()
alt Document exists
FS-->>App: doc.data()
App->>App: setUserData(doc.data())
else First login
App->>FS: userDoc.set({ email })
App->>App: setUserData({ email })
end
App->>U: Render "Welcome, email"
🧪 Try It Yourself
Task: Extend the Firestore integration to let signed-in users save a display name.
Add a displayName field to the Firestore document on first login and render it instead of the email address.
Starter snippet — replace the userDoc.set call in the useEffect:
// In the useEffect, replace the else branch:
} else {
const name = user.email.split('@')[0]; // derive a default display name
userDoc.set({ email: user.email, displayName: name });
setUserData({ email: user.email, displayName: name });
}
Then update the welcome text:
<Text>Welcome, {userData.displayName || userData.email}</Text>
Success criterion: Sign in for the first time with a new account. The welcome screen should show the portion of the email address before @ as the display name. Open the Firebase Console → Firestore → users collection to confirm the document was written with both email and displayName fields.
🔍 Checkpoint Quiz
Q1. Why is google-services.json placed in android/app/ rather than the project root, and what does @react-native-firebase/app use it for?
Q2. Given the following snippet, what happens if a user signs in for the very first time?
const userDoc = firestore().collection('users').doc(user.uid);
userDoc.get().then((doc) => {
if (doc.exists) {
setUserData(doc.data());
} else {
userDoc.set({ email: user.email });
setUserData({ email: user.email });
}
});
A) Nothing — doc.exists is always true after sign-in
B) The app crashes because doc.data() is called on a non-existent document
C) A new Firestore document is created with the user's email, and local state is seeded with it
D) The existing document is overwritten with the user's email
Q3. What is the purpose of the Firebase BOM (firebase-bom) declared in android/app/build.gradle?
A) It replaces google-services.json
B) It ensures all Firebase Android libraries use mutually compatible versions
C) It enables Firestore offline persistence
D) It registers the app in the Firebase Console automatically
Q4. How would you modify signOut to also clear the Firestore userData from state, and why is this important?
A1. The Android build system (Gradle) reads google-services.json from android/app/ during compilation to generate google-services.xml resource files that the native Firebase SDK uses at runtime. @react-native-firebase/app reads the compiled native configuration — so the file must be in the location Gradle expects, not the JS root.
A2. C — doc.exists is false for a brand-new user, so the else branch runs: userDoc.set() writes the document to Firestore and setUserData() seeds React state with { email: user.email }.
A3. B — The Firebase BOM pins a set of mutually compatible versions across all Firebase Android libraries. You declare the BOM version once and omit individual version numbers from each implementation line, preventing version-conflict bugs.
A4. Add setUserData({}) inside the signOut async function after setUser(null). Without it, if the component re-renders before unmounting (e.g., the user logs into a different account), stale userData from the previous session could flash on screen before the new Firestore fetch completes.
🪞 Recap
- Firebase is integrated into React Native Android by placing
google-services.jsoninandroid/app/and applying thecom.google.gms.google-servicesGradle plugin. - The
@react-native-firebase/appcore package plus individual service packages (auth,firestore) provide the JavaScript interface to Firebase services. - A dedicated
firebase.jsmodule centralizes service initialization and exports reusable instances. signInWithEmailAndPasswordandsignOutfromfirebase.auth()handle the full authentication lifecycle.- Firestore documents are keyed by
user.uidfor O(1) per-user lookups;doc.existsguards against overwriting existing records on repeat logins.
📚 Further Reading
- React Native Firebase official docs — the source of truth for all
@react-native-firebase/*packages and platform setup - Firebase Authentication docs — full reference for sign-in providers, session management, and security rules
- Firestore data model guide — explains collections, documents, subcollections, and query patterns
- Firebase BOM release notes — track compatible version sets across Firebase Android libraries
- ⬅️ Previous: Redux
- ➡️ Next: Generating Signed APK