How to Build a Real-Time Chat App with Firebase in 10 Minutes

1>How to Build a Real-Time Chat App with Firebase in 10 Minutes
Building a real-time chat application no longer requires a dedicated backend server, WebSocket management, or complex infrastructure. Firebase, Google’s Backend-as-a-Service platform, provides Firestore (a NoSQL database) and Authentication as managed services. Combined with the Firebase SDK, you can build a fully functional chat app in ten minutes. This guide assumes intermediate knowledge of JavaScript, basic HTML/CSS, and a Firebase account (free tier works). Target timeline: 10 minutes, excluding initial setup delays.
Step 1: Create a Firebase Project and Enable Firestore (1 minute)
- Go to the Firebase Console. Click Create a project (or select an existing one). Disable Google Analytics for speed.
- Once the project is ready, click Build > Firestore Database from the left sidebar.
- Click Create database, select Start in test mode (secure later), and choose a location nearest to your users.
- Wait 30 seconds for the database to provision. Test mode allows read/write to any authenticated user—sufficient for this tutorial.
Step 2: Set Up Authentication (1 minute)
- In the Firebase Console, go to Build > Authentication > Sign-in method.
- Enable Anonymous sign-in. This allows users to join without email/password, keeping the 10-minute constraint feasible. Click Save.
- Optionally, enable Email/Password or Google for richer authentication later. For this build, anonymous is enough to demonstrate real-time messaging.
Step 3: Register Your Web App (1 minute)
- In the Firebase Console, click the Project Overview > Web icon (
>) to register a web app. - Give your app a nickname (e.g., “ChatApp”) and leave “Firebase Hosting” unchecked.
- Copy the Firebase configuration object (a JavaScript object with
apiKey,authDomain,projectId, etc.). - Create a new folder on your computer called
firebase-chat. Inside, createindex.html,app.js, andstyles.css.
Step 4: Write the HTML Structure (2 minutes)
Open index.html and paste the following minimal structure. This creates a chat container with a message list and an input form.
Real-Time Chat · Firebase
Why Firebase v9 compat? The compat library mirrors v8 syntax, making it faster to write for beginners. Production apps should use modular v9, but compat is fine for a 10-minute build.
Step 5: Write the Application Logic (3 minutes)
Open app.js. First, paste your Firebase config (from Step 3) and initialize Firebase.
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_PROJECT.firebaseapp.com",
projectId: "YOUR_PROJECT",
storageBucket: "YOUR_PROJECT.appspot.com",
messagingSenderId: "123456789",
appId: "1:123456789:web:abcdef"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const db = firebase.firestore();Now, add anonymous authentication. When the page loads, sign in the user automatically.
// Sign in anonymously
auth.signInAnonymously().catch(error => {
console.error("Auth error:", error);
});Next, implement the message sending logic. Capture form submission, get the current user, and write to Firestore.
const form = document.getElementById('message-form');
const input = document.getElementById('message-input');
const messagesDiv = document.getElementById('messages');
form.addEventListener('submit', async (e) => {
e.preventDefault(); // prevent page reload
const text = input.value.trim();
if (!text) return;
const user = auth.currentUser;
if (!user) {
alert("Please wait, signing you in...");
return;
}
try {
await db.collection('messages').add({
text: text,
uid: user.uid,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
});
input.value = ''; // clear input
} catch (error) {
console.error("Error sending message:", error);
}
});Now, implement real-time message listening using onSnapshot. This is the core of the “real-time” feature.
// Listen for real-time updates
db.collection('messages')
.orderBy('createdAt', 'asc')
.limit(100)
.onSnapshot((snapshot) => {
// Clear the messages div before re-rendering (simplistic approach)
messagesDiv.innerHTML = '';
snapshot.forEach((doc) => {
const msg = doc.data();
const msgElement = document.createElement('div');
msgElement.textContent = msg.text;
msgElement.classList.add('message');
// Highlight own messages
if (msg.uid === auth.currentUser?.uid) {
msgElement.classList.add('own');
}
messagesDiv.appendChild(msgElement);
});
// Auto-scroll to bottom
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}, (error) => {
console.error("Snapshot error:", error);
});Key points about onSnapshot: This Firestore method establishes a persistent listener. Whenever a document is added, updated, or deleted in the messages collection, the callback fires instantly, updating the UI without polling. The orderBy('createdAt', 'asc') ensures messages appear in chronological order. The limit(100) prevents the UI from freezing if the chat history is huge.
Step 6: Style the Chat Interface (2 minutes)
Open styles.css and add the following. This creates a clean, mobile-friendly chat window.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#chat-container {
width: 400px;
max-width: 95%;
height: 600px;
background: white;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
#messages {
flex: 1;
overflow-y: auto;
padding: 16px;
background: #fafafa;
}
.message {
background: #e4e6eb;
padding: 8px 14px;
border-radius: 18px;
margin-bottom: 8px;
max-width: 80%;
word-wrap: break-word;
font-size: 15px;
line-height: 1.4;
}
.message.own {
background: #0b81ff;
color: white;
align-self: flex-end;
margin-left: auto;
}
#message-form {
display: flex;
padding: 12px;
border-top: 1px solid #ddd;
background: white;
}
#message-input {
flex: 1;
padding: 10px 16px;
border: 1px solid #ddd;
border-radius: 24px;
outline: none;
font-size: 15px;
}
button {
margin-left: 8px;
padding: 10px 20px;
background: #0b81ff;
color: white;
border: none;
border-radius: 24px;
font-size: 15px;
cursor: pointer;
transition: background 0.2s;
}
button:hover {
background: #0066cc;
}Why these styles? The .message.own class uses margin-left: auto to align user messages to the right, mimicking WhatsApp/Messenger paradigms. The container is fully responsive with max-width: 95%.
Step 7: Test the App (1 minute)
- Open
index.htmlin a modern browser (Chrome, Firefox, Edge). You do not need a local server—Firebase SDK loads from CDN, and Firestore handles data. - You should see an empty chat window. The anonymous sign-in happens silently. Type a message and click Send.
- The message should appear immediately. Open the same file in a second browser tab (or another device on the same network). Messages sent from one tab appear in the other in under 200ms (Firestore’s average latency).
- Check the Firebase Console > Firestore Database. You will see a
messagescollection with documents containingtext,uid, andcreatedAtfields.
Step 8: Security and Next-Level Optimizations
Firestore Security Rules: After testing, restrict access. In the Firebase Console > Firestore > Rules, replace the test rules with:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /messages/{messageId} {
allow read: if request.auth != null;
allow write: if request.auth != null
&& request.resource.data.text is string
&& request.resource.data.text.size() < 500;
}
}
}This ensures only authenticated users can read/write, and messages cannot exceed 500 characters (preventing spam).
Offline Persistence: Add firebase.firestore().enablePersistence() after initializing Firestore. This allows the chat to work offline and sync when connectivity returns.
User Display Names: Instead of just uid, you can prompt users for a nickname. Store it in localStorage and add it to each message document (e.g., displayName field). Display it above the message bubble.
Message Timestamps: The createdAt field is stored as a Firestore Timestamp. To display “2 minutes ago”, use a library like day.js or the built-in toDate() method.
Deleting Messages: Add a delete button (visible only to the message owner) that calls db.collection('messages').doc(docId).delete().
Troubleshooting Common Issues
- No messages appearing: Check the browser console for errors. Ensure the Firestore database is in test mode. Verify the Firebase config is correct and includes no typos.
- Messages not real-time: Confirm you used
onSnapshot, notget(). Theget()method fetches once;onSnapshotlistens continuously. - Authentication failed: Ensure Anonymous sign-in is enabled in Firebase Console. If you see “auth/operation-not-allowed”, go back to Authentication settings.
- Timestamp sorting issues: Firestore
serverTimestamp()is set on the server side. If you usenew Date()client-side, sorting may be unreliable across different time zones. Always preferserverTimestamp().
Performance and Scaling Notes
For a production chat app with thousands of concurrent users, consider the following:
- Use subcollections: Instead of one flat
messagescollection, structure it asrooms/{roomId}/messages/{messageId}for channel-based chat. - Paginate historical data: Use
limit()andstartAfter()to load older messages on scroll. - Use Firebase Cloud Functions to trigger notifications, moderate content, or generate message digests.
- Enable Firestore indexing for compound queries (e.g., filtering by room and sorting by timestamp).
Final Code Overview
Your final app.js file should be under 50 lines (excluding config). The entire application is three files: index.html, app.js, and styles.css. No build tools, no server, no npm install. This is the power of Firebase for real-time prototyping.





