Building a Food Delivery App with React Native and Firebase in 2025
Creating a food delivery app using React Native and Firebase is a powerful way to build a cross-platform mobile application that connects users with restaurants for seamless food ordering.
React Native enables developers to write a single codebase for both iOS and Android, while Firebase provides robust backend services like real-time databases, authentication, and cloud functions. This step-by-step guide will walk you through building a simple food delivery app in 2025, featuring restaurant browsing, food ordering, and order tracking, using the latest tools and packages. For a prebuilt solution, explore Instamobile’s UberEats clone template, which offers a production-ready React Native food delivery app integrated with Firebase.
This tutorial assumes basic knowledge of JavaScript and React Native. No prior Firebase experience is required. By the end, you’ll have a functional food delivery app with a modern UI, secure authentication, and real-time order updates, 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 (2024.3.2 or later): For Android development, with Android SDK 35 and NDK r25+.
- Xcode (16+): For iOS development (macOS only). Note: iOS builds must use Xcode 16+ (iOS 18 SDK) to comply with App Store submission requirements after April 24, 2025.
- Firebase Account: Sign up at firebase.google.com.
- A code editor like VS Code.
- Git: For cloning repositories or managing code.
Step 1: Set Up the React Native Project with Expo
-
Install Expo CLI: Install Expo CLI globally to streamline React Native development:
npm install -g expo-cli
-
Create a New Expo Project: Initialize a new project called
FoodDeliveryApp
:expo init FoodDeliveryApp
Choose the blank (TypeScript) template for a modern, type-safe setup. As of July 2025, Expo SDK 51 is the latest stable version, supporting React Native 0.75.4 with the New Architecture enabled.
-
Navigate to the Project Directory:
cd FoodDeliveryApp
-
Install Dependencies: Install the required packages for navigation, Firebase, and UI components:
npx expo install firebase@^11.10.0 @react-navigation/[email protected] @react-navigation/[email protected] [email protected] [email protected] [email protected] [email protected] react-native-reanimated@~3.15.0 [email protected]
[email protected]
: Latest Firebase SDK for 2025, supporting modular APIs.@react-navigation/*
: For screen navigation.react-native-elements
: Provides pre-built UI components for a polished interface, similar to those in Instamobile’s UberEats clone.expo-constants
,react-native-safe-area-context
,react-native-screens
: Required for Expo and navigation compatibility.react-native-reanimated@~3.15.0
: Ensures compatibility with Expo SDK 51 and React Native 0.75.4.react-native-vector-icons
: For icons in the UI.
-
Configure Expo for Compatibility: To avoid issues with older dependencies, update
app.json
to exclude incompatible versions ofreact-native-reanimated
:{
"expo": {
"install": {
"exclude": ["react-native-reanimated@~3.10.0"]
}
}
} -
Test the Project: Run the app to ensure the setup works:
npx expo start
Press
a
for Android ori
for iOS (macOS only) to launch the app on an emulator or device. You should see a blank Expo app.
Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount 🔥
Get the Mega BundleStep 2: Configure Firebase
-
Create a Firebase Project:
- Go to the Firebase Console.
- Click Add Project, name it
FoodDeliveryApp
, and follow the prompts to create it. - Click the Web icon under Project Overview to register a web app (even for mobile).
-
Enable Authentication:
- Navigate to Authentication > Sign-in method.
- Enable Email/Password as the sign-in provider, similar to authentication setups in Instaflutter templates.
-
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 restaurants, menu items, and orders, as used in Dopebase solutions.
-
Add Firebase Config to the Project:
- In the Firebase Console, go to Project Overview > Web app > Firebase SDK snippet.
- Copy the
firebaseConfig
object. - Create a file
src/firebase.ts
: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.json
file from Project Settings > Your apps > Android. Place it inFoodDeliveryApp/android/app/
. - iOS: Download the
GoogleService-Info.plist
file and place it inFoodDeliveryApp/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, restaurant list, menu, and order tracking screens. Create
src/navigation/AppNavigator.tsx
:import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import LoginScreen from '../screens/LoginScreen';
import SignupScreen from '../screens/SignupScreen';
import RestaurantListScreen from '../screens/RestaurantListScreen';
import MenuScreen from '../screens/MenuScreen';
import OrderTrackingScreen from '../screens/OrderTrackingScreen';
const Stack = createStackNavigator();
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="RestaurantList" component={RestaurantListScreen} />
<Stack.Screen name="Menu" component={MenuScreen} />
<Stack.Screen name="OrderTracking" component={OrderTrackingScreen} />
</Stack.Navigator>
</NavigationContainer>
);
} -
Update App Entry Point: Modify
App.tsx
to 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('RestaurantList');
} 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('RestaurantList');
} 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 Restaurant List Screen
-
Create Restaurant List Screen: Create
src/screens/RestaurantListScreen.tsx
to display a list of restaurants from Firestore:import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
import { collection, query, onSnapshot } from 'firebase/firestore';
import { db, auth } from '../firebase';
import { useNavigation } from '@react-navigation/native';
import { Button } from 'react-native-elements';
import { signOut } from 'firebase/auth';
export default function RestaurantListScreen() {
const [restaurants, setRestaurants] = useState([]);
const navigation = useNavigation();
useEffect(() => {
const q = query(collection(db, 'restaurants'));
const unsubscribe = onSnapshot(q, (snapshot) => {
const restaurantList = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setRestaurants(restaurantList);
});
return () => unsubscribe();
}, []);
const handleLogout = async () => {
await signOut(auth);
navigation.navigate('Login');
};
return (
<View style={styles.container}>
<Text style={styles.title}>Restaurants</Text>
<FlatList
data={restaurants}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.item}
onPress={() => navigation.navigate('Menu', { restaurantId: item.id })}
>
<Text style={styles.itemText}>{item.name}</Text>
</TouchableOpacity>
)}
/>
<Button title="Logout" onPress={handleLogout} containerStyle={styles.logoutButton} />
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20 },
title: { fontSize: 24, marginBottom: 20, textAlign: 'center' },
item: { padding: 15, borderBottomWidth: 1, borderBottomColor: '#ccc' },
itemText: { fontSize: 18 },
logoutButton: { marginTop: 20 },
}); -
Seed Restaurant Data: In the Firebase Console, create a
restaurants
collection with sample documents, e.g.:{
"name": "Pizza Palace",
"description": "Delicious pizzas and pastas",
"image": "https://example.com/pizza.jpg"
},
{
"name": "Burger Bonanza",
"description": "Juicy burgers and fries",
"image": "https://example.com/burger.jpg"
}
Step 6: Build the Menu Screen
-
Create Menu Screen: Create
src/screens/MenuScreen.tsx
to display a restaurant’s menu items:import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
import { collection, query, where, onSnapshot } from 'firebase/firestore';
import { db } from '../firebase';
import { useNavigation, useRoute } from '@react-navigation/native';
import { Button } from 'react-native-elements';
export default function MenuScreen() {
const [menuItems, setMenuItems] = useState([]);
const navigation = useNavigation();
const route = useRoute();
const { restaurantId } = route.params;
useEffect(() => {
const q = query(collection(db, 'menuItems'), where('restaurantId', '==', restaurantId));
const unsubscribe = onSnapshot(q, (snapshot) => {
const menuList = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setMenuItems(menuList);
});
return () => unsubscribe();
}, [restaurantId]);
const handleOrder = (item) => {
navigation.navigate('OrderTracking', { itemId: item.id, restaurantId });
};
return (
<View style={styles.container}>
<Text style={styles.title}>Menu</Text>
<FlatList
data={menuItems}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={styles.item}>
<Text style={styles.itemText}>{item.name} - ${item.price}</Text>
<Button
title="Order"
onPress={() => handleOrder(item)}
buttonStyle={styles.orderButton}
/>
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20 },
title: { fontSize: 24, marginBottom: 20, textAlign: 'center' },
item: { padding: 15, borderBottomWidth: 1, borderBottomColor: '#ccc', flexDirection: 'row', justifyContent: 'space-between' },
itemText: { fontSize: 18 },
orderButton: { backgroundColor: '#28a745' },
}); -
Seed Menu Data: In the Firebase Console, create a
menuItems
collection with sample documents, e.g.:{
"restaurantId": "RESTAURANT_ID",
"name": "Margherita Pizza",
"price": 12.99,
"description": "Classic pizza with tomato and mozzarella"
},
{
"restaurantId": "RESTAURANT_ID",
"name": "Cheeseburger",
"price": 8.99,
"description": "Beef patty with cheddar and lettuce"
}Replace
RESTAURANT_ID
with the ID of a restaurant document.
Step 7: Build the Order Tracking Screen
-
Create Order Tracking Screen: Create
src/screens/OrderTrackingScreen.tsx
to track orders in real-time:import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { collection, addDoc, doc, onSnapshot } from 'firebase/firestore';
import { db, auth } from '../firebase';
import { useRoute } from '@react-navigation/native';
export default function OrderTrackingScreen() {
const [order, setOrder] = useState(null);
const route = useRoute();
const { itemId, restaurantId } = route.params;
useEffect(() => {
// Simulate placing an order
const placeOrder = async () => {
const orderRef = await addDoc(collection(db, 'orders'), {
userId: auth.currentUser.uid,
itemId,
restaurantId,
status: 'Preparing',
createdAt: new Date(),
});
const unsubscribe = onSnapshot(doc(db, 'orders', orderRef.id), (doc) => {
setOrder({ id: doc.id, ...doc.data() });
});
return () => unsubscribe();
};
placeOrder();
}, [itemId, restaurantId]);
return (
<View style={styles.container}>
<Text style={styles.title}>Order Tracking</Text>
{order ? (
<View>
<Text style={styles.text}>Order ID: {order.id}</Text>
<Text style={styles.text}>Status: {order.status}</Text>
</View>
) : (
<Text style={styles.text}>Placing order...</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, justifyContent: 'center' },
title: { fontSize: 24, marginBottom: 20, textAlign: 'center' },
text: { fontSize: 18, marginBottom: 10 },
}); -
Explanation of Order Tracking:
- Firestore Integration: The
useEffect
hook places an order and listens for real-time updates to the order’s status usingonSnapshot
, similar to features in Instamobile’s UberEats clone. - Cleanup: The
unsubscribe
function prevents memory leaks by stopping the Firestore listener when the component unmounts. - Order Simulation: For simplicity, the order is placed automatically. In a production app, integrate a cart system and payment gateway like Stripe.
- Firestore Integration: The
-
Seed Order Data: In the Firebase Console, you can manually update the
orders
collection to simulate status changes (e.g.,Preparing
→Out for Delivery
→Delivered
) for testing.
Step 8: Secure Firestore Rules
Update Firestore rules in the Firebase Console to ensure secure access to data:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /restaurants/{document=**} {
allow read: if true;
allow write: if request.auth != null;
}
match /menuItems/{document=**} {
allow read: if true;
allow write: if request.auth != null;
}
match /orders/{document=**} {
allow read: if request.auth != null && request.auth.uid == resource.data.userId;
allow write: if request.auth != null
&& request.resource.data.userId == request.auth.uid
&& request.resource.data.itemId is string
&& request.resource.data.restaurantId is string
&& request.resource.data.status is string
&& request.resource.data.createdAt is timestamp;
}
}
}
Note: Before deploying to production, ensure these rules validate field types and timestamps to maintain data integrity, as seen in Dopebase templates.
Step 9: Style and Enhance the App
-
Add Tailwind CSS (Optional): For better styling, install
tailwind-react-native-classnames
:npm install [email protected]
Note: Tailwind support in React Native is evolving. You may explore
tailwind-react-native-classnames
orUnoCSS
for an alternative with better developer experience. UpdateLoginScreen.tsx
,SignupScreen.tsx
, and other screens with Tailwind classes:import tw from 'tailwind-react-native-classnames';
const styles = StyleSheet.create({
container: [tw`flex-1 justify-center p-5`],
title: [tw`text-2xl mb-5 text-center`],
input: [tw`border p-2 mb-2 rounded`],
}); -
Add Icons: Use
react-native-vector-icons
for icons in the restaurant and menu screens:import Icon from 'react-native-vector-icons/MaterialIcons';
// Example in RestaurantListScreen.tsx
<Icon name="restaurant" size={24} color="#000" />
Step 10: Build and Test the App
-
Run on Android:
npx expo start
Press
a
to launch on an Android emulator or device (API level 21+). -
Run on iOS (macOS only): Press
i
to launch on an iOS simulator, ensuring Xcode 16+ is used for iOS 18 SDK compliance. -
Test the App:
- Sign up or log in with an email and password.
- Browse restaurants and select a menu item to place an order.
- Track the order status in real-time, similar to features in Instamobile’s UberEats clone.
- Verify users in the Firebase Console under Authentication > Users.
- Check data in Firestore Database for
restaurants
,menuItems
, andorders
collections. - Test logout and navigation between screens.
Step 11: Enhance the App
- Add Cart System: Implement a cart to allow multiple item orders, as seen in Instaflutter templates.
- Integrate Payments: Use Stripe or Firebase Cloud Functions for payment processing.
- Add Location Services: Integrate
expo-location
for restaurant filtering by proximity. - Push Notifications: Implement Firebase Cloud Messaging (FCM) for order status updates, as included in Dopebase solutions.
Troubleshooting Tips
- Firebase Errors: Ensure
google-services.json
andGoogleService-Info.plist
are correctly placed. Check the Firebase Console for correct API keys. - Real-Time Not Working: Verify Firestore rules and ensure
onSnapshot
is set up correctly. - Build Issues: Confirm Expo SDK 51, Node.js, and
react-native-reanimated@~3.15.0
versions are compatible. Runnpm install
orpod install
if dependencies fail. - Performance: Optimize
FlatList
withinitialNumToRender
andwindowSize
props for large restaurant or menu lists.
Looking for a custom mobile application?
Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.
Get in TouchConclusion
You’ve built a food delivery app using React Native and Firebase, leveraging the latest 2025 tools like Expo SDK 51, Firebase 11.10.0, and react-native-elements
. This app supports user authentication, restaurant browsing, food ordering, and real-time order tracking, making it a solid foundation for further enhancements. For faster development, explore prebuilt solutions like Instamobile’s UberEats clone 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.
For further help, join the Firebase community on Stack Overflow
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