Firebase (Firestore)

The pick whenever your data needs to update live - chat, feeds, collaboration.

Firestore is Google's hosted document database. Its headline feature is that a web page can subscribe to a query and receive changes the moment they happen - no WebSocket server, no polling, no backend code at all.


Who gets this pick

Every time you answer that your data is real-time (chat, live feeds), the database result is Firestore - regardless of scale, hosting or experience. Real-time sync is the hardest thing on that list to add yourself, so it decides the answer.


Why Firestore fits

Live updates are the default. Call onSnapshot() on a query and your callback runs immediately with the current results, then again whenever a matching document is added, changed or removed - including changes made by other users.

No server to run. The browser talks to Firestore directly, so a real-time app can be nothing more than static files. That's why this works even on shared hosting.

Offline support. Firestore's client SDKs can cache data locally and queue writes while offline, syncing when the connection returns.

Everything around it. Firebase Authentication, Hosting and Cloud Storage share the same project and console, so sign-in and file uploads don't need separate services.


A first project: a live chat room

Our real-time chat guide builds a working chat in about 40 lines of JavaScript, plus the security rules that stop strangers from abusing it. Then extend it:

  1. Add Firebase Authentication (anonymous or Google sign-in) and store each message's author uid.
  2. Tighten the rules so users can only create messages as themselves and delete only their own.
  3. Add rooms as separate collections or a roomId field, and query by it.

Modelling data for Firestore

Firestore is not a relational database, and the biggest beginner mistake is designing tables first. Instead, start from the screens: list the queries each page needs, then shape documents so each query reads as few documents as possible. Duplicating a little data - storing an author's display name on each message, say - is normal and expected. There are no joins, and some queries need a composite index; when they do, the error message in your console includes a link that creates it for you.


How billing works

On the free Spark plan you get a daily allowance of document reads, writes and deletes plus storage (at the time of writing, 50,000 reads and 20,000 writes per day). Beyond that, the pay-as-you-go Blaze plan bills per operation. The rule of thumb: every document a query returns is a read, and so is every document a listener receives when it changes. Always limit() your queries, paginate long lists, and set a budget alert if you upgrade.


Mistakes to avoid

  • Leaving test-mode rules in place. Test mode allows anyone to read and write everything until it expires. Write real rules before you share the URL.
  • Thinking the config object is secret. It isn't - it identifies your project and is meant to be public. Security rules are what protect your data.
  • Unbounded listeners on whole collections, which get slower and more expensive as data grows.
  • Huge documents. A document is limited to 1 MiB; keep growing lists in a subcollection, not an array field.
  • Forgetting to unsubscribe listeners when a component or page goes away.

Is Supabase a better fit?

If you want real-time updates and relational data with SQL - joins, constraints, reporting - Supabase is a strong alternative built on PostgreSQL. Firestore's client SDK is simpler for a first real-time app; Supabase gives you more room if your data is naturally relational.


Take the quiz All picks