# Realtime Listeners and Offline Persistence — Firebase

Source: https://www.skillbyai.com/en/firebase/qu-live

> onSnapshot and the local cache.

## Live data with a local cache

`onSnapshot` attaches a **listener** to a document or query: it fires immediately with current data and again whenever results change, and returns an unsubscribe function you must call when the screen goes away. Local writes trigger listeners at once with `metadata.hasPendingWrites` set (latency compensation) before the server confirms. Mobile SDKs keep an offline cache by default; on the web, enable a persistent cache with `initializeFirestore` and `persistentLocalCache` (API details vary by SDK version, check the docs), so reads work offline and writes are queued until the connection returns. Each change delivered to a listener counts as reads, so scope listeners tightly.

## A live list with an offline cache

TypeScript.

```typescript
import {
  initializeFirestore, persistentLocalCache, persistentMultipleTabManager,
  collection, query, where, orderBy, limit, onSnapshot,
} from "firebase/firestore";

const db = initializeFirestore(app, {
  localCache: persistentLocalCache({ tabManager: persistentMultipleTabManager() }),
});

const q = query(
  collection(db, "rooms", roomId, "messages"),
  orderBy("createdAt", "desc"),
  limit(50),
);

const unsubscribe = onSnapshot(q, (snap) => {
  const messages = snap.docs.map((d) => ({
    id: d.id,
    ...d.data(),
    pending: d.metadata.hasPendingWrites,
  }));
  render(messages);
}, (err) => console.error("listener failed", err));

// when leaving the screen
unsubscribe();
```

## Always unsubscribe

Forgotten listeners keep connections open and keep billing reads; tie them to component lifecycle hooks.

**Quiz:** What does onSnapshot return?

- [ ] A promise of the first snapshot only
- [x] A function that removes the listener
- [ ] The document id
- [ ] A transaction object

*Answer:* A function that removes the listener. Call it when the view is destroyed.
