Building a Chat Application with React Native and Firebase
Build React Native chat on Firebase with secure membership rules, paginated messages, optimistic sends, attachment controls, presence tradeoffs, and push notifications. This revised guide emphasizes supported APIs, production tradeoffs, and an upgrade-friendly path without tying the advice to a calendar year.

Modern implementation baseline
Build React Native chat on Firebase with secure membership rules, paginated messages, optimistic sends, attachment controls, presence tradeoffs, and push notifications.
Use modular Firebase APIs, server-enforced Security Rules, explicit data models, and observable background jobs. Authentication state is not authorization: every database and storage path still needs a least-privilege rule.
Recommended approach
- Model conversations and membership for authorization first.
- Paginate messages and avoid unbounded listeners.
- Make sends idempotent and reconcile delivery state.
Production checklist
- Test rules with the emulator suite and denied requests.
- Design indexes and pagination before production traffic.
- Handle offline writes, retries, idempotency, and subscription cleanup.
Authoritative references
This tutorial assumes basic knowledge of JavaScript and React Native. No prior Firebase experience is required. By the end, you’ll have a functional chat app with a modern UI, real-time messaging, and secure authentication, ready for iOS and Android deployment.
Prerequisites
Ensure you have the following installed before starting:
- Node.js (v20.x or later, LTS recommended): Download from nodejs.org.
- npm (v10.x or later, included with Node.js).
- Expo CLI: For streamlined React Native project setup.
- Android Studio: Use the current stable release with compatible Android SDK and NDK tooling.
- Xcode: Use the current stable release and SDK required by App Store Connect.
- Firebase Account: Sign up at firebase.google.com.
- A code editor like VS Code.
- Git: For cloning repositories or managing code.
Step-by-Step Guide to Building the Chat App
Step 1: Set Up the React Native Project with Expo
-
Create a TypeScript Expo project:
npx create-expo-app@latest ChatApp --template blank-typescript
cd ChatApp -
Install compatible dependencies:
npx expo install firebase @react-navigation/native @react-navigation/native-stack react-native-gifted-chat expo-constants react-native-safe-area-context react-native-screens react-native-reanimatedExpo resolves native package versions that match the selected SDK. Review the chat library's New Architecture support before shipping.
-
Test the project: Run the app to ensure the setup works:
npx expo startPress
afor Android orifor iOS (macOS only) to launch the app on an emulator or device. You should see a blank Expo app.
Step 2: Configure Firebase
-
Create a Firebase Project:
- Go to the Firebase Console.
- Click Add Project, name it
ChatApp, and follow the prompts to create it. - Once created, click the Web icon under Project Overview to register a web app (even for mobile).
-
Enable Authentication:
- In the Firebase Console, navigate to Authentication > Sign-in method.
- Enable Email/Password as the sign-in provider to match features in templates available at Instaflutter.
-
Set Up Firestore:
- Navigate to Firestore Database > Create Database.
- Choose Start in test mode (for development) and select a region (e.g.,
us-central1). - This creates a real-time NoSQL database for storing messages, similar to solutions offered by Dopebase.
-
Add Firebase Config to the Project:
- In the Firebase Console, go to Project Overview > Web app > Firebase SDK snippet.
- Copy the
firebaseConfigobject. - Create a file
src/firebase.tsin your project:Replace the placeholders with your Firebase project’s config values.import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
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"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
export { auth, db };
-
Add Firebase to Android and iOS:
- Android: Download the
google-services.jsonfile from Project Settings > Your apps > Android. Place it inChatApp/android/app/. - iOS: Download the
GoogleService-Info.plistfile and place it inChatApp/ios/. Update the iOS project by running:cd ios && pod install - In
android/build.gradle, add:dependencies {
classpath 'com.google.gms:google-services:4.4.2'
} - In
android/app/build.gradle, add at the bottom:apply plugin: 'com.google.gms.google-services'
- Android: Download the
Step 3: Set Up Navigation
-
Configure Navigation: Create a navigation stack for the login, signup, and chat screens. Create
src/navigation/AppNavigator.tsx:import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import LoginScreen from '../screens/LoginScreen';
import SignupScreen from '../screens/SignupScreen';
import ChatScreen from '../screens/ChatScreen';
const Stack = createNativeStackNavigator();
export default function AppNavigator() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Signup" component={SignupScreen} />
<Stack.Screen name="Chat" component={ChatScreen} />
</Stack.Navigator>
</NavigationContainer>
);
} -
Update App Entry Point: Modify
App.tsxto use the navigator:import React from 'react';
import AppNavigator from './src/navigation/AppNavigator';
export default function App() {
return <AppNavigator />;
}
Step 4: Implement Authentication Screens
-
Create Login Screen: Create
src/screens/LoginScreen.tsx:import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, Alert } from 'react-native';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../firebase';
import { useNavigation } from '@react-navigation/native';
export default function LoginScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const navigation = useNavigation();
const handleLogin = async () => {
try {
await signInWithEmailAndPassword(auth, email, password);
navigation.navigate('Chat');
} catch (error) {
Alert.alert('Login Failed', error.message);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Login</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Login" onPress={handleLogin} />
<Button
title="Go to Signup"
onPress={() => navigation.navigate('Signup')}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 20 },
title: { fontSize: 24, marginBottom: 20, textAlign: 'center' },
input: { borderWidth: 1, padding: 10, marginBottom: 10, borderRadius: 5 },
}); -
Create Signup Screen: Create
src/screens/SignupScreen.tsx:import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, Alert } from 'react-native';
import { createUserWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../firebase';
import { useNavigation } from '@react-navigation/native';
export default function SignupScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const navigation = useNavigation();
const handleSignup = async () => {
try {
await createUserWithEmailAndPassword(auth, email, password);
navigation.navigate('Chat');
} catch (error) {
Alert.alert('Signup Failed', error.message);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Sign Up</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Sign Up" onPress={handleSignup} />
<Button
title="Go to Login"
onPress={() => navigation.navigate('Login')}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 20 },
title: { fontSize: 24, marginBottom: 20, textAlign: 'center' },
input: { borderWidth: 1, padding: 10, marginBottom: 10, borderRadius: 5 },
});
Step 5: Build the Chat Screen
-
Create Chat Screen: Create
src/screens/ChatScreen.tsxusingreact-native-gifted-chatfor a polished chat UI and Firebase Firestore for real-time messaging, inspired by solutions at Dopebase:import React, { useState, useEffect, useCallback } from 'react';
import { GiftedChat } from 'react-native-gifted-chat';
import { collection, addDoc, query, orderBy, onSnapshot } from 'firebase/firestore';
import { auth, db } from '../firebase';
export default function ChatScreen() {
const [messages, setMessages] = useState([]);
useEffect(() => {
const q = query(collection(db, 'messages'), orderBy('createdAt', 'desc'));
const unsubscribe = onSnapshot(q, (snapshot) => {
const messageList = snapshot.docs.map((doc) => ({
_id: doc.id,
text: doc.data().text,
createdAt: doc.data().createdAt.toDate(),
user: doc.data().user,
}));
setMessages(messageList);
});
return () => unsubscribe(); // Cleanup listener on unmount
}, []);
const onSend = useCallback(async (newMessages = []) => {
const message = newMessages[0];
await addDoc(collection(db, 'messages'), {
text: message.text,
createdAt: new Date(),
user: {
_id: auth.currentUser.uid,
name: auth.currentUser.email,
},
});
}, []);
return (
<GiftedChat
messages={messages}
onSend={(messages) => onSend(messages)}
user={{
_id: auth.currentUser.uid,
name: auth.currentUser.email,
}}
/>
);
} -
Explanation of Chat Logic:
- Firestore Integration: The
useEffecthook listens for changes in themessagescollection usingonSnapshot, updating the UI in real-time, as implemented in templates at Instaflutter. - Message Format: Messages are stored in Firestore with
text,createdAt, anduserfields, formatted forreact-native-gifted-chat. - Cleanup: The
unsubscribefunction prevents memory leaks by stopping the Firestore listener when the component unmounts. - Sending Messages: The
onSendfunction adds new messages to Firestore, triggering real-time updates across all clients.
- Firestore Integration: The
Step 6: Style and Enhance the App
-
Add Tailwind CSS (Optional): For better styling, install
react-native-tailwindcss:npm install react-native-tailwindcss@1.0.0Update
LoginScreen.tsxandSignupScreen.tsxstyles using Tailwind classes:import { t } from 'react-native-tailwindcss';
const styles = StyleSheet.create({
container: [t.flex1, t.justifyCenter, t.p5],
title: [t.text2xl, t.mB5, t.textCenter],
input: [t.border, t.p2, t.mB2, t.rounded],
}); -
Add Logout Functionality: Update
ChatScreen.tsxto include a logout button:import { View, Button } from 'react-native';
import { signOut } from 'firebase/auth';
import { useNavigation } from '@react-navigation/native';
// Inside ChatScreen
const navigation = useNavigation();
const handleLogout = async () => {
await signOut(auth);
navigation.navigate('Login');
};
// Add to the GiftedChat render
<View style={{ padding: 10 }}>
<Button title="Logout" onPress={handleLogout} />
</View> -
Secure Firestore Rules: Update Firestore rules in the Firebase Console to ensure only authenticated users can read/write messages. For production, use stricter rules to validate field types and timestamps:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /messages/{document=**} {
allow read: if request.auth != null;
allow write: if request.auth != null
&& request.resource.data.text is string
&& request.resource.data.createdAt is timestamp
&& request.resource.data.user._id == request.auth.uid;
}
}
}Note: Before deploying to production, update these rules to ensure data integrity, such as requiring valid field types and timestamps.
Step 7: Build and Test the App
-
Run on Android:
npx expo startPress
ato launch on an Android emulator or device (API level 21+). -
Run on iOS (macOS only): Press
ito launch on an iOS simulator, ensuring Xcode 16+ is used for iOS 18 SDK compliance. -
Test the App:
- Sign up with a new email and password.
- Log in and send messages, which should appear in real-time, similar to features in Dopebase templates.
- Verify new users in the Firebase Console under Authentication > Users.
- Check messages in Firestore Database > messages collection.
- Test logout and navigation between screens.
Step 8: Enhance the App
- Add Media Sharing: Use Firebase Storage to upload images/videos, integrating with
react-native-image-picker, as seen in Instamobile’s video chat app. - Improve UI: Customize
react-native-gifted-chatprops (e.g.,renderBubble,renderAvatar) for a unique look. - Private Chats: Create sub-collections in Firestore for one-on-one chats, filtering by user IDs.
- Notifications: Implement Firebase Cloud Messaging (FCM) for push notifications, as included in templates at Instaflutter.
Troubleshooting Tips
- Firebase Errors: Ensure
google-services.jsonandGoogleService-Info.plistare correctly placed. Check the Firebase Console for correct API keys. - Real-Time Not Working: Verify Firestore rules and ensure
onSnapshotis set up correctly. - Build Issues: Run
npx expo-doctorand confirm that Node.js, Expo, React Native, and Reanimated versions are compatible. Runnpm installorpod installif dependencies fail. - Performance: Use
react-native-gifted-chat’sscrollToBottomprop for smooth scrolling with many messages.
Conclusion
You’ve built a real-time chat application using React Native and Firebase with modular APIs, secure rules, pagination, and a maintained chat UI layer. This app supports user authentication, real-time messaging, and cross-platform deployment, making it a solid foundation for further enhancements. For faster development, explore prebuilt solutions like Instamobile’s video chat app template or customizable templates at Dopebase and Instaflutter, which offer production-ready codebases. Dive into Firebase’s documentation for advanced features like Cloud Functions or Storage, and check out React Native’s community resources at reactnative.dev for additional libraries.
Additional Resources
- dopebase.com - Mobile app development resources and templates
- instamobile.io - Explore our production-ready React Native templates
- instaflutter.com - Check out our Flutter templates for cross-platform development
- iosapptemplates.com - Premium iOS app templates for quick development