Firebase provides a powerful backend solution for React applications, offering authentication, realtime databases, file storage, and more. Here’s how to integrate Firebase with your React app:
1. Firebase Setup
Install Firebase SDK
npm install firebase
# or
yarn add firebase
Initialize Firebase
// src/firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_SENDER_ID",
appId: "YOUR_APP_ID"
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
export const storage = getStorage(app);
2. Authentication
Email/Password Authentication
import { auth } from "./firebase";
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut
} from "firebase/auth";
// Sign up
const signUp = async (email, password) => {
try {
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
console.log("User created:", userCredential.user);
} catch (error) {
console.error("Signup error:", error.message);
}
};
// Sign in
const signIn = async (email, password) => {
try {
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
console.log("User signed in:", userCredential.user);
} catch (error) {
console.error("Login error:", error.message);
}
};
// Sign out
const logOut = async () => {
try {
await signOut(auth);
console.log("User signed out");
} catch (error) {
console.error("Logout error:", error.message);
}
};
Google Authentication
import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";
const signInWithGoogle = async () => {
const provider = new GoogleAuthProvider();
try {
const result = await signInWithPopup(auth, provider);
console.log("Google user:", result.user);
} catch (error) {
console.error("Google sign-in error:", error.message);
}
};
3. Firestore Database
CRUD Operations
import { db } from "./firebase";
import {
collection,
addDoc,
getDocs,
doc,
updateDoc,
deleteDoc
} from "firebase/firestore";
// Add document
const addPost = async (title, content) => {
try {
const docRef = await addDoc(collection(db, "posts"), {
title,
content,
createdAt: new Date()
});
console.log("Document written with ID:", docRef.id);
} catch (error) {
console.error("Error adding document:", error);
}
};
// Read documents
const getPosts = async () => {
const querySnapshot = await getDocs(collection(db, "posts"));
const posts = [];
querySnapshot.forEach((doc) => {
posts.push({ id: doc.id, ...doc.data() });
});
return posts;
};
// Update document
const updatePost = async (id, newData) => {
try {
await updateDoc(doc(db, "posts", id), newData);
console.log("Document updated");
} catch (error) {
console.error("Error updating document:", error);
}
};
// Delete document
const deletePost = async (id) => {
try {
await deleteDoc(doc(db, "posts", id));
console.log("Document deleted");
} catch (error) {
console.error("Error deleting document:", error);
}
};
4. Realtime Updates
Subscribe to Data Changes
import { onSnapshot } from "firebase/firestore";
const setupPostsListener = () => {
const unsubscribe = onSnapshot(collection(db, "posts"), (snapshot) => {
const posts = [];
snapshot.forEach((doc) => {
posts.push({ id: doc.id, ...doc.data() });
});
console.log("Current posts:", posts);
});
// Call unsubscribe() to stop listening when needed
return unsubscribe;
};
5. File Storage
Upload Files
import { storage } from "./firebase";
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
const uploadFile = async (file) => {
try {
// Create a storage reference
const storageRef = ref(storage, `files/${file.name}`);
// Upload file
const snapshot = await uploadBytes(storageRef, file);
console.log("Uploaded file:", snapshot.metadata);
// Get download URL
const downloadURL = await getDownloadURL(storageRef);
console.log("File available at:", downloadURL);
return downloadURL;
} catch (error) {
console.error("Upload failed:", error);
throw error;
}
};
6. Authentication State Management
React Context for Auth
// AuthContext.js
import { createContext, useContext, useEffect, useState } from "react";
import { auth } from "./firebase";
import { onAuthStateChanged } from "firebase/auth";
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
loading
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}
Protected Routes
import { useAuth } from "./AuthContext";
import { Navigate } from "react-router-dom";
function ProtectedRoute({ children }) {
const { currentUser } = useAuth();
if (!currentUser) {
return <Navigate to="/login" replace />;
}
return children;
}
7. Security Rules
Example Firestore Rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /posts/{postId} {
allow read: if true;
allow create: if request.auth != null;
allow update, delete: if request.auth != null &&
request.auth.uid == resource.data.authorId;
}
}
}
Example Storage Rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /files/{fileName} {
allow read: if true;
allow write: if request.auth != null &&
request.resource.size < 5 * 1024 * 1024;
}
}
}
Best Practices
- Environment Variables: Store Firebase config in
.env
file
REACT_APP_FIREBASE_API_KEY=your_api_key
REACT_APP_FIREBASE_AUTH_DOMAIN=your_auth_domain
- Error Handling: Implement comprehensive error handling
- Loading States: Show loading indicators during async operations
- Security: Always implement proper security rules
- Modularization: Organize Firebase services into separate modules
- TypeScript: Use Firebase with TypeScript for better type safety
- Performance: Optimize queries with indexes and pagination
This comprehensive integration covers the core Firebase services you’ll typically need in a React application. Remember to enable the Firebase services you need in the Firebase Console before implementing them in your app.