TL;DR: Firebase is Google’s backend-as-a-service platform, and pairing it with a modern web stack lets you ship authenticated, database-backed, real-time web apps without managing servers. This tutorial walks through creating a Firebase project, wiring up Authentication, modeling data in Firestore, deploying with Firebase Hosting, and adding live real-time updates, then closes with concrete next actions to harden your app for production.
Overview
01What Firebase Web Development Actually Involves
Firebase is a suite of managed backend services (authentication, a NoSQL database, file storage, static hosting, cloud functions, and analytics) all accessible from client-side JavaScript with minimal server code. For web developers, this means you can build a fully functional, data-driven application using only front-end code plus the Firebase SDK, while Google handles scaling, uptime, and infrastructure.
This guide focuses on the core path most teams need first: get a project running, let users sign in, store and query structured data, publish the site, and make part of the UI update in real time as data changes. Each section below builds on the previous one, so a fresh Firebase project can go from empty to a working real-time app by the end.
Who this is for: developers comfortable with HTML/CSS/JavaScript who want a managed backend instead of writing and hosting their own API server. No prior Firebase or Node.js backend experience is assumed.
Backend-as-a-Service
No server code to write or deploy for most CRUD-style apps, the SDK talks directly to managed Google infrastructure over authenticated, rules-protected channels.
Real-Time by Default
Firestore and Realtime Database both push changes to connected clients instantly, without you writing WebSocket or polling logic yourself.
Generous Free Tier
The Spark plan covers Auth, a Firestore read/write quota, and Hosting bandwidth at no cost, enough for most prototypes and small production apps.
Framework-Agnostic
The web SDK works the same whether your front end is plain JavaScript, React, Vue, Angular, or Svelte, it’s just a client library.
Firebase Setup
02Creating and Configuring Your Firebase Project
Every Firebase feature lives inside a project, which you create once in the Firebase console and then reference from your app via a configuration object. Setup involves three parts: creating the project, registering a web app inside it, and installing the SDK locally.
- Create a project. Go to the Firebase console, click “Add project,” name it, and optionally enable Google Analytics. Project creation takes under a minute and provisions a unique project ID used across every Firebase service.
- Register a web app. Inside the project, click the web icon (</>) under “Add app.” Give it a nickname; Firebase Hosting setup is optional at this stage and can be added later. This step generates the
firebaseConfigobject your app needs. - Install the SDK. In your project directory, run
npm install firebasefor a bundler-based workflow, or reference the CDN script tags directly if you’re not using a build step. - Initialize the app. Import the config and call
initializeApp()once, near the entry point of your application, before any other Firebase service is used.
| File | Purpose | Notes |
|---|---|---|
| firebaseConfig object | Connects your app to your specific Firebase project | Safe to expose client-side; it is not a secret key |
| firebase.json | Deployment and Hosting configuration | Generated by the Firebase CLI, not the console |
| .firebaserc | Maps local project alias to your Firebase project ID | Lets you target staging vs. production projects |
| firestore.rules | Security rules controlling read/write access | Defaults to locked-down; must be edited deliberately |
// firebase-init.js
import { initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project",
storageBucket: "your-project.appspot.com",
messagingSenderId: "SENDER_ID",
appId: "APP_ID"
};
const app = initializeApp(firebaseConfig);
export default app;
Key point: the Firebase CLI (npm install -g firebase-tools, then firebase login and firebase init) is separate from the web SDK. You need the CLI for deploying Hosting, Functions, and security rules from your terminal, the web SDK alone only lets your app talk to Firebase at runtime.
Authentication
03Adding User Authentication
Firebase Authentication handles sign-up, sign-in, session persistence, and password resets without you building any of that logic yourself. It supports email/password, phone, and OAuth providers like Google, Facebook, and GitHub, all through a consistent API.
Email & Password
Enable in the console under Authentication → Sign-in method. Use createUserWithEmailAndPassword() and signInWithEmailAndPassword() from the Auth SDK.
OAuth Providers
Google, GitHub, and Facebook sign-in use signInWithPopup() or signInWithRedirect() with a provider object, e.g. GoogleAuthProvider().
Session State
onAuthStateChanged() fires whenever sign-in status changes, letting you update the UI reactively across page loads and tabs.
Security Rules Link
Once a user is authenticated, request.auth.uid becomes available in Firestore and Storage security rules for per-user access control.
import { getAuth, onAuthStateChanged, signInWithEmailAndPassword } from "firebase/auth";
const auth = getAuth(app);
onAuthStateChanged(auth, (user) => {
if (user) {
console.log("Signed in as", user.uid);
} else {
console.log("No user signed in");
}
});
signInWithEmailAndPassword(auth, email, password)
.catch((error) => console.error(error.code, error.message));
Firestore Database
04Modeling and Querying Data with Firestore
Cloud Firestore is Firebase’s flexible NoSQL document database. Data is organized into collections of documents, where each document holds key-value fields and can contain nested sub-collections. Unlike a relational database, there’s no fixed schema, but consistent structure across documents in a collection still matters for queries to work predictably.
- Design your collections. Group data by how it’s queried, not just how it’s related. A “posts” collection with a “comments” sub-collection per post is a common pattern.
- Write data. Use
addDoc()for auto-generated IDs orsetDoc()withdoc()when you need a specific document ID. - Read data. Use
getDoc()for a single document orgetDocs()with aquery()for filtered, ordered, or limited results. - Set security rules. Restrict reads and writes in
firestore.rulesbased on authentication state and document ownership before going to production.
| Operation | Method | Typical Use |
|---|---|---|
| Create | addDoc(collectionRef, data) |
Add a new document with an auto-generated ID |
| Read (single) | getDoc(docRef) |
Fetch one document by known ID |
| Read (query) | getDocs(query(collectionRef, where(...))) |
Filter and sort a collection |
| Update | updateDoc(docRef, fields) |
Modify specific fields without overwriting the whole document |
| Delete | deleteDoc(docRef) |
Remove a document permanently |
import { getFirestore, collection, addDoc, query, where, getDocs } from "firebase/firestore";
const db = getFirestore(app);
// Add a document
await addDoc(collection(db, "posts"), {
title: "Hello Firestore",
authorId: auth.currentUser.uid,
createdAt: Date.now()
});
// Query documents
const q = query(collection(db, "posts"), where("authorId", "==", auth.currentUser.uid));
const snapshot = await getDocs(q);
snapshot.forEach((doc) => console.log(doc.id, doc.data()));
Common pitfall: leaving Firestore in “test mode” (open read/write for 30 days) past initial development. Lock down firestore.rules before any real users touch the app, test mode rules expire automatically and will silently break your app in production if you forget to replace them.
Hosting
05Deploying with Firebase Hosting
Firebase Hosting serves your static assets (HTML, CSS, JS, and any build output) over a global CDN with free SSL, and integrates directly with the Firebase CLI for one-command deploys. It also handles single-page-app rewrites, custom domains, and rollback to previous releases.
- Initialize Hosting. Run
firebase init hostingin your project root, select your Firebase project, and specify your public/build directory (e.g.distorbuild). - Configure rewrites for SPAs. If using a client-side router (React Router, Vue Router), set
"rewrites": [{"source": "**", "destination": "/index.html"}]infirebase.jsonso refreshing deep links doesn’t 404. - Build your app. Run your framework’s build command (
npm run build) to produce static output in the configured directory. - Deploy. Run
firebase deploy --only hosting. The CLI uploads changed files only, and prints a live*.web.appURL on success.
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "/.*", "/node_modules/**"],
"rewrites": [
{ "source": "**", "destination": "/index.html" }
]
}
}
Custom domains: under Hosting → Add custom domain in the console, Firebase walks you through DNS verification (TXT record) and then automatic SSL provisioning, typically live within 24 hours, often much sooner.
Real-time Features
06Building Live, Real-Time Updates
Firestore’s onSnapshot() listener is what separates Firebase apps from typical REST-backed apps: instead of polling for changes, your UI subscribes to a query or document and receives updates the instant data changes anywhere, including from other users, other devices, or server-side writes.
Live Queries
onSnapshot(query(...), callback) re-runs the callback with a fresh snapshot every time matching data changes.
Offline Support
Firestore caches data locally by default, so listeners keep working offline and sync automatically when connectivity returns.
Cleanup
onSnapshot() returns an unsubscribe function, always call it when a component unmounts to avoid memory leaks and stale listeners.
Realtime Database Alt.
For extremely high-frequency data (presence, cursors, gameplay), the older Realtime Database’s flatter JSON tree can outperform Firestore.
import { onSnapshot, collection, query, orderBy } from "firebase/firestore";
const q = query(collection(db, "posts"), orderBy("createdAt", "desc"));
const unsubscribe = onSnapshot(q, (snapshot) => {
const posts = snapshot.docs.map((doc) => ({ id: doc.id...doc.data() }));
renderPosts(posts); // update your UI here
});
// Later, e.g. on component unmount:
unsubscribe();
Action
07Next Steps to Ship a Production-Ready App
With Auth, Firestore, Hosting, and real-time listeners in place, you have a working full-stack web app. Before calling it production-ready, work through these hardening steps.
| Task | Why It Matters |
|---|---|
| Tighten Firestore & Storage security rules | Prevents unauthorized reads/writes once test-mode rules expire |
| Add Firebase App Check | Blocks abusive traffic and API scraping from non-app clients |
| Set budget alerts in Google Cloud Console | Firestore and Functions billing scales with usage; alerts prevent surprise bills |
| Add Cloud Functions for sensitive logic | Keeps privileged operations (payments, admin actions) off the client |
| Enable Firebase Performance Monitoring | Surfaces slow queries and rendering bottlenecks in real user sessions |
Action: start by auditing your firestore.rules file against your actual access patterns, then run firebase deploy --only firestore:rules to push the update, this single step closes the most common Firebase security gap before you add any other production hardening.
Dev Station works with teams across the United States and the United Kingdom. Application data is held to SOC 2 or HIPAA where a US client requires it, and to GDPR with ISO 27001 for UK and EU records. Our engineers work from Vietnam with overlap into US Eastern, US Pacific and UK GMT hours, and we invoice in USD or GBP.
Want an AI assistant to summarize or cite this guide?
Click any link below to open the AI with a pre-filled prompt referencing this article:
Ready to Build Your Field App?
Contact Dev Station Technology to discuss your project requirements and receive a development roadmap within 48 hours.
Get a Quote →


