Get realtime updates with Cloud Firestore Stay organized with collections Save and categorize content based on your preferences.
You canlisten to a document with theonSnapshot() method. An initialcall using the callback you provide creates a document snapshot immediately withthe current contents of the single document. Then, each timethe contents change, another call updates the document snapshot.
- Pipeline operations don't support realtime listeners. To use realtime listeners with Enterprise edition databases, use core operations as described on this page.
- Realtime listeners are not supported in the PHP client library.
Web
import{doc,onSnapshot}from"firebase/firestore";constunsub=onSnapshot(doc(db,"cities","SF"),(doc)=>{console.log("Current data: ",doc.data());});
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.db.collection("cities").doc("SF").onSnapshot((doc)=>{console.log("Current data: ",doc.data());});
Swift
db.collection("cities").document("SF").addSnapshotListener{documentSnapshot,erroringuardletdocument=documentSnapshotelse{print("Error fetching document:\(error!)")return}guardletdata=document.data()else{print("Document data was empty.")return}print("Current data:\(data)")}
Objective-C
[[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"SF"]addSnapshotListener:^(FIRDocumentSnapshot*snapshot,NSError*error){if(snapshot==nil){NSLog(@"Error fetching document: %@",error);return;}NSLog(@"Current data: %@",snapshot.data);}];
Kotlin
valdocRef=db.collection("cities").document("SF")docRef.addSnapshotListener{snapshot,e->if(e!=null){Log.w(TAG,"Listen failed.",e)return@addSnapshotListener}if(snapshot!=null &&snapshot.exists()){Log.d(TAG,"Current data:${snapshot.data}")}else{Log.d(TAG,"Current data: null")}}
Java
finalDocumentReferencedocRef=db.collection("cities").document("SF");docRef.addSnapshotListener(newEventListener<DocumentSnapshot>(){@OverridepublicvoidonEvent(@NullableDocumentSnapshotsnapshot,@NullableFirebaseFirestoreExceptione){if(e!=null){Log.w(TAG,"Listen failed.",e);return;}if(snapshot!=null &&snapshot.exists()){Log.d(TAG,"Current data: "+snapshot.getData());}else{Log.d(TAG,"Current data: null");}}});
Dart
finaldocRef=db.collection("cities").doc("SF");docRef.snapshots().listen((event)=>print("current data:${event.data()}"),onError:(error)=>print("Listen failed:$error"),);
Often, you want your UI to react to changes in the contents of a Firestore document or collection. You can do so with aStreamBuilder widget that consumes the Firestore snapshot stream:
classUserInformationextendsStatefulWidget{@override_UserInformationStatecreateState()=>_UserInformationState();}class_UserInformationStateextendsState<UserInformation>{finalStream<QuerySnapshot>_usersStream=FirebaseFirestore.instance.collection('users').snapshots();@overrideWidgetbuild(BuildContextcontext){returnStreamBuilder<QuerySnapshot>(stream:_usersStream,builder:(BuildContextcontext,AsyncSnapshot<QuerySnapshot>snapshot){if(snapshot.hasError){returnconstText('Something went wrong');}if(snapshot.connectionState==ConnectionState.waiting){returnconstText("Loading");}returnListView(children:snapshot.data!.docs.map((DocumentSnapshotdocument){Map<String,dynamic>data=document.data()!asMap<String,dynamic>;returnListTile(title:Text(data['full_name']),subtitle:Text(data['company']),);}).toList().cast(),);},);}}
Java
DocumentReferencedocRef=db.collection("cities").document("SF");docRef.addSnapshotListener(newEventListener<DocumentSnapshot>(){@OverridepublicvoidonEvent(@NullableDocumentSnapshotsnapshot,@NullableFirestoreExceptione){if(e!=null){System.err.println("Listen failed: "+e);return;}if(snapshot!=null &&snapshot.exists()){System.out.println("Current data: "+snapshot.getData());}else{System.out.print("Current data: null");}}});Python
# Create an Event for notifying main thread.callback_done=threading.Event()# Create a callback on_snapshot function to capture changesdefon_snapshot(doc_snapshot,changes,read_time):fordocindoc_snapshot:print(f"Received document snapshot:{doc.id}")callback_done.set()doc_ref=db.collection("cities").document("SF")# Watch the documentdoc_watch=doc_ref.on_snapshot(on_snapshot)C++
DocumentReferencedoc_ref=db->Collection("cities").Document("SF");doc_ref.AddSnapshotListener([](constDocumentSnapshot&snapshot,Errorerror,conststd::string&errorMsg){if(error==Error::kErrorOk){if(snapshot.exists()){std::cout <<"Current data: " <<snapshot <<std::endl;}else{std::cout <<"Current data: null" <<std::endl;}}else{std::cout <<"Listen failed: " <<error <<std::endl;}});
Node.js
constdoc=db.collection('cities').doc('SF');constobserver=doc.onSnapshot(docSnapshot=>{console.log(`Received doc snapshot:${docSnapshot}`);// ...},err=>{console.log(`Encountered error:${err}`);});Go
import("context""fmt""io""time""cloud.google.com/go/firestore""google.golang.org/grpc/codes""google.golang.org/grpc/status")// listenDocument listens to a single document.funclistenDocument(ctxcontext.Context,wio.Writer,projectID,collectionstring)error{// projectID := "project-id"// Сontext with timeout stops listening to changes.ctx,cancel:=context.WithTimeout(ctx,30*time.Second)defercancel()client,err:=firestore.NewClient(ctx,projectID)iferr!=nil{returnfmt.Errorf("firestore.NewClient: %w",err)}deferclient.Close()it:=client.Collection(collection).Doc("SF").Snapshots(ctx)for{snap,err:=it.Next()// DeadlineExceeded will be returned when ctx is cancelled.ifstatus.Code(err)==codes.DeadlineExceeded{returnnil}iferr!=nil{returnfmt.Errorf("Snapshots.Next: %w",err)}if!snap.Exists(){fmt.Fprintf(w,"Document no longer exists\n")returnnil}fmt.Fprintf(w,"Received document snapshot: %v\n",snap.Data())}}PHP
// Not supported in the PHP client libraryUnity
DocumentReferencedocRef=db.Collection("cities").Document("SF");docRef.Listen(snapshot=>{Debug.Log("Callback received document snapshot.");Debug.Log(String.Format("Document data for {0} document:",snapshot.Id));Dictionary<string,object>city=snapshot.ToDictionary();foreach(KeyValuePair<string,object>pairincity){Debug.Log(String.Format("{0}: {1}",pair.Key,pair.Value));}});
C#
DocumentReferencedocRef=db.Collection("cities").Document("SF");FirestoreChangeListenerlistener=docRef.Listen(snapshot=>{Console.WriteLine("Callback received document snapshot.");Console.WriteLine("Document exists? {0}",snapshot.Exists);if(snapshot.Exists){Console.WriteLine("Document data for {0} document:",snapshot.Id);Dictionary<string,object>city=snapshot.ToDictionary();foreach(KeyValuePair<string,object>pairincity){Console.WriteLine("{0}: {1}",pair.Key,pair.Value);}}});Ruby
doc_ref=firestore.col(collection_path).docdocument_pathsnapshots=[]# Watch the document.listener=doc_ref.listendo|snapshot|puts"Received document snapshot:#{snapshot.document_id}"snapshots <<snapshotendEvents for local changes
Local writes in your app will invoke snapshot listeners immediately.This is because of an important feature called "latency compensation."When you perform a write, your listeners will be notified with the newdatabefore the data is sent to the backend.
Retrieved documents have ametadata.hasPendingWrites property that indicateswhether the document has local changes that haven't been written to the backendyet. You can use this property to determine the source of events received byyour snapshot listener:
Web
import{doc,onSnapshot}from"firebase/firestore";constunsub=onSnapshot(doc(db,"cities","SF"),(doc)=>{constsource=doc.metadata.hasPendingWrites?"Local":"Server";console.log(source," data: ",doc.data());});
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.db.collection("cities").doc("SF").onSnapshot((doc)=>{varsource=doc.metadata.hasPendingWrites?"Local":"Server";console.log(source," data: ",doc.data());});
Swift
db.collection("cities").document("SF").addSnapshotListener{documentSnapshot,erroringuardletdocument=documentSnapshotelse{print("Error fetching document:\(error!)")return}letsource=document.metadata.hasPendingWrites?"Local":"Server"print("\(source) data:\(document.data()??[:])")}
Objective-C
[[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"SF"]addSnapshotListener:^(FIRDocumentSnapshot*snapshot,NSError*error){if(snapshot==nil){NSLog(@"Error fetching document: %@",error);return;}NSString*source=snapshot.metadata.hasPendingWrites?@"Local":@"Server";NSLog(@"%@ data: %@",source,snapshot.data);}];
Kotlin
valdocRef=db.collection("cities").document("SF")docRef.addSnapshotListener{snapshot,e->if(e!=null){Log.w(TAG,"Listen failed.",e)return@addSnapshotListener}valsource=if(snapshot!=null &&snapshot.metadata.hasPendingWrites()){"Local"}else{"Server"}if(snapshot!=null &&snapshot.exists()){Log.d(TAG,"$source data:${snapshot.data}")}else{Log.d(TAG,"$source data: null")}}
Java
finalDocumentReferencedocRef=db.collection("cities").document("SF");docRef.addSnapshotListener(newEventListener<DocumentSnapshot>(){@OverridepublicvoidonEvent(@NullableDocumentSnapshotsnapshot,@NullableFirebaseFirestoreExceptione){if(e!=null){Log.w(TAG,"Listen failed.",e);return;}Stringsource=snapshot!=null &&snapshot.getMetadata().hasPendingWrites()?"Local":"Server";if(snapshot!=null &&snapshot.exists()){Log.d(TAG,source+" data: "+snapshot.getData());}else{Log.d(TAG,source+" data: null");}}});
Dart
finaldocRef=db.collection("cities").doc("SF");docRef.snapshots().listen((event){finalsource=(event.metadata.hasPendingWrites)?"Local":"Server";print("$source data:${event.data()}");},onError:(error)=>print("Listen failed:$error"),);
Java
#NotyetsupportedintheJavaclientlibrary
Python
//NotyetsupportedinPythonclientlibrary
C++
DocumentReferencedoc_ref=db->Collection("cities").Document("SF");doc_ref.AddSnapshotListener([](constDocumentSnapshot&snapshot,Errorerror,conststd::string&errorMsg){if(error==Error::kErrorOk){constchar*source=snapshot.metadata().has_pending_writes()?"Local":"Server";if(snapshot.exists()){std::cout <<source <<" data: " <<snapshot.Get("name").string_value() <<std::endl;}else{std::cout <<source <<" data: null" <<std::endl;}}else{std::cout <<"Listen failed: " <<error <<std::endl;}});
Node.js
// Not yet supported in the Node.js client libraryGo
// Not yet supported in the Go client libraryPHP
// Not supported in the PHP client libraryUnity
DocumentReferencedocRef=db.Collection("cities").Document("SF");docRef.Listen(snapshot=>{stringsource=(snapshot!=null &&snapshot.Metadata.HasPendingWrites)?"Local":"Server";stringsnapshotData="null";if(snapshot!=null &&snapshot.Exists){System.Text.StringBuilderbuilder=newSystem.Text.StringBuilder();IDictionary<string,object>dict=snapshot.ToDictionary();foreach(varKVPairindict){builder.Append($"{KVPair.Key}: {KVPair.Value}\n");}snapshotData=builder.ToString();}Debug.Log($"{source} data: ${snapshotData}");});
C#
// Not yet supported in the C# client libraryRuby
//NotyetsupportedintheRubyclientlibrary
Events for metadata changes
When listening for changes to a document, collection, or query, you can passoptions to control the granularity of events that your listener will receive.
By default, listeners are not notified of changes that only affect metadata.Consider what happens when your app writes a new document:
- A change event is immediately fired with the new data. The document has not yet been written to the backend so the "pending writes" flag is
true. - The document is written to the backend.
- The backend notifies the client of the successful write. There is no change to the document data, but there is a metadata change because the "pending writes" flag is now
false.
If you want to receive snapshot events when the document or query metadatachanges, pass a listen options object when attaching your listener.
Note: You can pass listener options as shown in the following samples.You can also use the configuration interfaces for snapshot optionsdescribed below to explicitly configure events formetadata changes. For more information, see the reference documentation forKotlin + KTX Android,Java Android,Swift,Objective-C,andWeb modular.Web
import{doc,onSnapshot}from"firebase/firestore";constunsub=onSnapshot(doc(db,"cities","SF"),{includeMetadataChanges:true},(doc)=>{// ...});
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.db.collection("cities").doc("SF").onSnapshot({// Listen for document metadata changesincludeMetadataChanges:true},(doc)=>{// ...});
Swift
// Listen to document metadata.db.collection("cities").document("SF").addSnapshotListener(includeMetadataChanges:true){documentSnapshot,errorin// ...}
Objective-C
// Listen for metadata changes.[[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"SF"]addSnapshotListenerWithIncludeMetadataChanges:YESlistener:^(FIRDocumentSnapshot*snapshot,NSError*error){// ...}];
Kotlin
// Listen for metadata changes to the document.valdocRef=db.collection("cities").document("SF")docRef.addSnapshotListener(MetadataChanges.INCLUDE){snapshot,e->// ...}
Java
// Listen for metadata changes to the document.DocumentReferencedocRef=db.collection("cities").document("SF");docRef.addSnapshotListener(MetadataChanges.INCLUDE,newEventListener<DocumentSnapshot>(){@OverridepublicvoidonEvent(@NullableDocumentSnapshotsnapshot,@NullableFirebaseFirestoreExceptione){// ...}});
Dart
finaldocRef=db.collection("cities").doc("SF");docRef.snapshots(includeMetadataChanges:true).listen((event){// ...});
Java
// Not yet supported in the Java client libraryPython
//NotyetsupportedinPythonclientlibrary
C++
DocumentReferencedoc_ref=db->Collection("cities").Document("SF");doc_ref.AddSnapshotListener(MetadataChanges::kInclude,[](constDocumentSnapshot&snapshot,Errorerror,conststd::string&errorMsg){/* ... */});
Node.js
// Not yet supported the Node.js client libraryGo
// Not yet supported in the Go client libraryPHP
// Not supported in the PHP client libraryUnity
DocumentReferencedocRef=db.Collection("cities").Document("SF");docRef.Listen(MetadataChanges.Include,snapshot=>{// ...});
C#
// Not yet supported in the C# client libraryRuby
//NotyetsupportedintheRubyclientlibrary
hasPendingWrites. In JavaScript, usethePromise returned from your write operation by attaching a.then()callback. In Swift, pass a completion callback to your write function.Configure listeners for local changes only
Cloud Firestore snapshot listeners take an initial snapshot from the local cacheand concurrently fetch corresponding data from the server.
In some cases, you may not want follow-up fetches from the server. Client SDKsallow you to configure listeners to fire only with respect to data in the localcache. This helps you avoid unnecessary server calls and their costs, andleverage the client-side cache, which reflects local data and mutations.
Here, snapshot options are set in client code to allow listening for localchanges only.
Web
Learn more about the tree-shakeable modular Web API andupgrade from the namespaced API.constunsubscribe=onSnapshot(doc(db,"cities","SF"),{includeMetadataChanges:true,source:'cache'},(documentSnapshot)=>{//…});
Web
Learn more about the tree-shakeable modular Web API andupgrade from the namespaced API.// Not yet supported in the Web namespaced APISwift
// Set up listener optionsletoptions=SnapshotListenOptions().withSource(ListenSource.cache).withIncludeMetadataChanges(true)db.collection("cities").document("SF").addSnapshotListener(options:options){documentSnapshot,errorin// ...}
Objective-C
// Set up listener optionsFIRSnapshotListenOptions*options=[[FIRSnapshotListenOptionsalloc]init];FIRSnapshotListenOptions*optionsWithSourceAndMetadata=[[optionsoptionsWithIncludeMetadataChanges:YES]optionsWithSource:FIRListenSourceCache];[[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"SF"]addSnapshotListenerWithOptions:optionsWithSourceAndMetadatalistener:^(FIRDocumentSnapshot*snapshot,NSError*error){//…}];
Kotlin
// Set up listener optionsvaloptions=SnapshotListenOptions.Builder().setMetadataChanges(MetadataChanges.INCLUDE).setSource(ListenSource.CACHE).build();db.collection("cities").document("SF").addSnapshotListener(options){snapshot,error->//…}
Java
// Set up listener optionsSnapshotListenOptionsoptions=newSnapshotListenOptions.Builder().setMetadataChanges(MetadataChanges.INCLUDE).setSource(ListenSource.CACHE).build();db.collection("cities").document("SF").addSnapshotListener(options,newEventListener<DocumentSnapshot>(){//…});
Dart
// Not yet supported in this client library
Java
#NotyetsupportedintheJavaclientlibrary
Python
//NotyetsupportedinPythonclientlibrary
C++
// Not yet supported in the C++ client libraryNode.js
// Not yet supported in the Node.js client libraryGo
// Not yet supported in the Go client libraryPHP
// Not yet supported in the PHP client libraryUnity
// Not yet supported in the Unity client libraryC#
// Not yet supported in the C# client libraryRuby
//NotyetsupportedintheRubyclientlibrary
Listen to multiple documents in a collection
As with documents, you can useonSnapshot() instead ofget() to listen to theresults of a query. This creates a query snapshot. For example, to listen to thedocuments with stateCA:
Web
import{collection,query,where,onSnapshot}from"firebase/firestore";constq=query(collection(db,"cities"),where("state","==","CA"));constunsubscribe=onSnapshot(q,(querySnapshot)=>{constcities=[];querySnapshot.forEach((doc)=>{cities.push(doc.data().name);});console.log("Current cities in CA: ",cities.join(", "));});
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.db.collection("cities").where("state","==","CA").onSnapshot((querySnapshot)=>{varcities=[];querySnapshot.forEach((doc)=>{cities.push(doc.data().name);});console.log("Current cities in CA: ",cities.join(", "));});
Swift
db.collection("cities").whereField("state",isEqualTo:"CA").addSnapshotListener{querySnapshot,erroringuardletdocuments=querySnapshot?.documentselse{print("Error fetching documents:\(error!)")return}letcities=documents.compactMap{$0["name"]}print("Current cities in CA:\(cities)")}
Objective-C
[[[self.dbcollectionWithPath:@"cities"]queryWhereField:@"state"isEqualTo:@"CA"]addSnapshotListener:^(FIRQuerySnapshot*snapshot,NSError*error){if(snapshot==nil){NSLog(@"Error fetching documents: %@",error);return;}NSMutableArray*cities=[NSMutableArrayarray];for(FIRDocumentSnapshot*documentinsnapshot.documents){[citiesaddObject:document.data[@"name"]];}NSLog(@"Current cities in CA: %@",cities);}];
Kotlin
db.collection("cities").whereEqualTo("state","CA").addSnapshotListener{value,e->if(e!=null){Log.w(TAG,"Listen failed.",e)return@addSnapshotListener}valcities=ArrayList<String>()for(docinvalue!!){doc.getString("name")?.let{cities.add(it)}}Log.d(TAG,"Current cites in CA:$cities")}
Java
db.collection("cities").whereEqualTo("state","CA").addSnapshotListener(newEventListener<QuerySnapshot>(){@OverridepublicvoidonEvent(@NullableQuerySnapshotvalue,@NullableFirebaseFirestoreExceptione){if(e!=null){Log.w(TAG,"Listen failed.",e);return;}List<String>cities=newArrayList<>();for(QueryDocumentSnapshotdoc:value){if(doc.get("name")!=null){cities.add(doc.getString("name"));}}Log.d(TAG,"Current cites in CA: "+cities);}});
Dart
db.collection("cities").where("state",isEqualTo:"CA").snapshots().listen((event){finalcities=[];for(vardocinevent.docs){cities.add(doc.data()["name"]);}print("cities in CA:${cities.join(", ")}");});
Java
db.collection("cities").whereEqualTo("state","CA").addSnapshotListener(newEventListener<QuerySnapshot>(){@OverridepublicvoidonEvent(@NullableQuerySnapshotsnapshots,@NullableFirestoreExceptione){if(e!=null){System.err.println("Listen failed:"+e);return;}List<String>cities=newArrayList<>();for(DocumentSnapshotdoc:snapshots){if(doc.get("name")!=null){cities.add(doc.getString("name"));}}System.out.println("Current cites in CA: "+cities);}});Python
# Create an Event for notifying main thread.callback_done=threading.Event()# Create a callback on_snapshot function to capture changesdefon_snapshot(col_snapshot,changes,read_time):print("Callback received query snapshot.")print("Current cities in California:")fordocincol_snapshot:print(f"{doc.id}")callback_done.set()col_query=db.collection("cities").where(filter=FieldFilter("state","==","CA"))# Watch the collection queryquery_watch=col_query.on_snapshot(on_snapshot)C++
db->Collection("cities").WhereEqualTo("state",FieldValue::String("CA")).AddSnapshotListener([](constQuerySnapshot&snapshot,Errorerror,conststd::string&errorMsg){if(error==Error::kErrorOk){std::vector<std::string>cities;std::cout <<"Current cities in CA: " <<error <<std::endl;for(constDocumentSnapshot&doc:snapshot.documents()){cities.push_back(doc.Get("name").string_value());std::cout <<"" <<cities.back() <<std::endl;}}else{std::cout <<"Listen failed: " <<error <<std::endl;}});
Node.js
constquery=db.collection('cities').where('state','==','CA');constobserver=query.onSnapshot(querySnapshot=>{console.log(`Received query snapshot of size${querySnapshot.size}`);// ...},err=>{console.log(`Encountered error:${err}`);});Go
import("context""fmt""io""time""cloud.google.com/go/firestore""google.golang.org/api/iterator""google.golang.org/grpc/codes""google.golang.org/grpc/status")// listenMultiple listens to a query, returning the names of all cities// for a state.funclistenMultiple(ctxcontext.Context,wio.Writer,projectID,collectionstring)error{// projectID := "project-id"ctx,cancel:=context.WithTimeout(ctx,30*time.Second)defercancel()client,err:=firestore.NewClient(ctx,projectID)iferr!=nil{returnfmt.Errorf("firestore.NewClient: %w",err)}deferclient.Close()it:=client.Collection(collection).Where("state","==","CA").Snapshots(ctx)for{snap,err:=it.Next()// DeadlineExceeded will be returned when ctx is cancelled.ifstatus.Code(err)==codes.DeadlineExceeded{returnnil}iferr!=nil{returnfmt.Errorf("Snapshots.Next: %w",err)}ifsnap!=nil{for{doc,err:=snap.Documents.Next()iferr==iterator.Done{break}iferr!=nil{returnfmt.Errorf("Documents.Next: %w",err)}fmt.Fprintf(w,"Current cities in California: %v\n",doc.Ref.ID)}}}}PHP
// Not supported in the PHP client libraryUnity
Queryquery=db.Collection("cities").WhereEqualTo("State","CA");ListenerRegistrationlistener=query.Listen(snapshot=>{Debug.Log("Callback received query snapshot.");Debug.Log("Current cities in California:");foreach(DocumentSnapshotdocumentSnapshotinsnapshot.Documents){Debug.Log(documentSnapshot.Id);}});
C#
CollectionReferencecitiesRef=db.Collection("cities");Queryquery=db.Collection("cities").WhereEqualTo("State","CA");FirestoreChangeListenerlistener=query.Listen(snapshot=>{Console.WriteLine("Callback received query snapshot.");Console.WriteLine("Current cities in California:");foreach(DocumentSnapshotdocumentSnapshotinsnapshot.Documents){Console.WriteLine(documentSnapshot.Id);}});Ruby
query=firestore.col(collection_path).where:state,:==,"CA"docs=[]# Watch the collection query.listener=query.listendo|snapshot|puts"Callback received query snapshot."puts"Current cities in California:"snapshot.docs.eachdo|doc|putsdoc.document_iddocs <<docendendThe snapshot handler will receive a new query snapshot every time the queryresults change (that is, when a document is added, removed, or modified).
Important: As explained above underEvents for localchanges, you will receive eventsimmediately for yourlocal writes. Your listener can use themetadata.hasPendingWrites field oneach document to determine whether the document has local changes that have notyet been written to the backend.View changes between snapshots
It is often useful to see the actual changes to query results between querysnapshots, instead of simply using the entire query snapshot. For example, youmay want to maintain a cache as individual documents are added, removed, andmodified.
Web
import{collection,query,where,onSnapshot}from"firebase/firestore";constq=query(collection(db,"cities"),where("state","==","CA"));constunsubscribe=onSnapshot(q,(snapshot)=>{snapshot.docChanges().forEach((change)=>{if(change.type==="added"){console.log("New city: ",change.doc.data());}if(change.type==="modified"){console.log("Modified city: ",change.doc.data());}if(change.type==="removed"){console.log("Removed city: ",change.doc.data());}});});
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.db.collection("cities").where("state","==","CA").onSnapshot((snapshot)=>{snapshot.docChanges().forEach((change)=>{if(change.type==="added"){console.log("New city: ",change.doc.data());}if(change.type==="modified"){console.log("Modified city: ",change.doc.data());}if(change.type==="removed"){console.log("Removed city: ",change.doc.data());}});});
Swift
db.collection("cities").whereField("state",isEqualTo:"CA").addSnapshotListener{querySnapshot,erroringuardletsnapshot=querySnapshotelse{print("Error fetching snapshots:\(error!)")return}snapshot.documentChanges.forEach{diffinif(diff.type==.added){print("New city:\(diff.document.data())")}if(diff.type==.modified){print("Modified city:\(diff.document.data())")}if(diff.type==.removed){print("Removed city:\(diff.document.data())")}}}
Objective-C
[[[self.dbcollectionWithPath:@"cities"]queryWhereField:@"state"isEqualTo:@"CA"]addSnapshotListener:^(FIRQuerySnapshot*snapshot,NSError*error){if(snapshot==nil){NSLog(@"Error fetching documents: %@",error);return;}for(FIRDocumentChange*diffinsnapshot.documentChanges){if(diff.type==FIRDocumentChangeTypeAdded){NSLog(@"New city: %@",diff.document.data);}if(diff.type==FIRDocumentChangeTypeModified){NSLog(@"Modified city: %@",diff.document.data);}if(diff.type==FIRDocumentChangeTypeRemoved){NSLog(@"Removed city: %@",diff.document.data);}}}];
Kotlin
db.collection("cities").whereEqualTo("state","CA").addSnapshotListener{snapshots,e->if(e!=null){Log.w(TAG,"listen:error",e)return@addSnapshotListener}for(dcinsnapshots!!.documentChanges){when(dc.type){DocumentChange.Type.ADDED->Log.d(TAG,"New city:${dc.document.data}")DocumentChange.Type.MODIFIED->Log.d(TAG,"Modified city:${dc.document.data}")DocumentChange.Type.REMOVED->Log.d(TAG,"Removed city:${dc.document.data}")}}}
Java
db.collection("cities").whereEqualTo("state","CA").addSnapshotListener(newEventListener<QuerySnapshot>(){@OverridepublicvoidonEvent(@NullableQuerySnapshotsnapshots,@NullableFirebaseFirestoreExceptione){if(e!=null){Log.w(TAG,"listen:error",e);return;}for(DocumentChangedc:snapshots.getDocumentChanges()){switch(dc.getType()){caseADDED:Log.d(TAG,"New city: "+dc.getDocument().getData());break;caseMODIFIED:Log.d(TAG,"Modified city: "+dc.getDocument().getData());break;caseREMOVED:Log.d(TAG,"Removed city: "+dc.getDocument().getData());break;}}}});
Dart
db.collection("cities").where("state",isEqualTo:"CA").snapshots().listen((event){for(varchangeinevent.docChanges){switch(change.type){caseDocumentChangeType.added:print("New City:${change.doc.data()}");break;caseDocumentChangeType.modified:print("Modified City:${change.doc.data()}");break;caseDocumentChangeType.removed:print("Removed City:${change.doc.data()}");break;}}});
Java
db.collection("cities").whereEqualTo("state","CA").addSnapshotListener(newEventListener<QuerySnapshot>(){@OverridepublicvoidonEvent(@NullableQuerySnapshotsnapshots,@NullableFirestoreExceptione){if(e!=null){System.err.println("Listen failed: "+e);return;}for(DocumentChangedc:snapshots.getDocumentChanges()){switch(dc.getType()){caseADDED:System.out.println("New city: "+dc.getDocument().getData());break;caseMODIFIED:System.out.println("Modified city: "+dc.getDocument().getData());break;caseREMOVED:System.out.println("Removed city: "+dc.getDocument().getData());break;default:break;}}}});C++
db->Collection("cities").WhereEqualTo("state",FieldValue::String("CA")).AddSnapshotListener([](constQuerySnapshot&snapshot,Errorerror,conststd::string&errorMsg){if(error==Error::kErrorOk){for(constDocumentChange&dc:snapshot.DocumentChanges()){switch(dc.type()){caseDocumentChange::Type::kAdded:std::cout <<"New city: " <<dc.document().Get("name").string_value() <<std::endl;break;caseDocumentChange::Type::kModified:std::cout <<"Modified city: " <<dc.document().Get("name").string_value() <<std::endl;break;caseDocumentChange::Type::kRemoved:std::cout <<"Removed city: " <<dc.document().Get("name").string_value() <<std::endl;break;}}}else{std::cout <<"Listen failed: " <<error <<std::endl;}});
Python
# Create an Event for notifying main thread.delete_done=threading.Event()# Create a callback on_snapshot function to capture changesdefon_snapshot(col_snapshot,changes,read_time):print("Callback received query snapshot.")print("Current cities in California: ")forchangeinchanges:ifchange.type.name=="ADDED":print(f"New city:{change.document.id}")elifchange.type.name=="MODIFIED":print(f"Modified city:{change.document.id}")elifchange.type.name=="REMOVED":print(f"Removed city:{change.document.id}")delete_done.set()col_query=db.collection("cities").where(filter=FieldFilter("state","==","CA"))# Watch the collection queryquery_watch=col_query.on_snapshot(on_snapshot)Node.js
constobserver=db.collection('cities').where('state','==','CA').onSnapshot(querySnapshot=>{querySnapshot.docChanges().forEach(change=>{if(change.type==='added'){console.log('New city: ',change.doc.data());}if(change.type==='modified'){console.log('Modified city: ',change.doc.data());}if(change.type==='removed'){console.log('Removed city: ',change.doc.data());}});});Go
import("context""fmt""io""time""cloud.google.com/go/firestore""google.golang.org/grpc/codes""google.golang.org/grpc/status")// listenChanges listens to a query, returning the list of document changes.funclistenChanges(ctxcontext.Context,wio.Writer,projectID,collectionstring)error{// projectID := "project-id"ctx,cancel:=context.WithTimeout(ctx,30*time.Second)defercancel()client,err:=firestore.NewClient(ctx,projectID)iferr!=nil{returnfmt.Errorf("firestore.NewClient: %w",err)}deferclient.Close()it:=client.Collection(collection).Where("state","==","CA").Snapshots(ctx)for{snap,err:=it.Next()// DeadlineExceeded will be returned when ctx is cancelled.ifstatus.Code(err)==codes.DeadlineExceeded{returnnil}iferr!=nil{returnfmt.Errorf("Snapshots.Next: %w",err)}ifsnap!=nil{for_,change:=rangesnap.Changes{switchchange.Kind{casefirestore.DocumentAdded:fmt.Fprintf(w,"New city: %v\n",change.Doc.Data())casefirestore.DocumentModified:fmt.Fprintf(w,"Modified city: %v\n",change.Doc.Data())casefirestore.DocumentRemoved:fmt.Fprintf(w,"Removed city: %v\n",change.Doc.Data())}}}}}PHP
// Not supported in the PHP client libraryUnity
Queryquery=db.Collection("cities").WhereEqualTo("State","CA");ListenerRegistrationlistener=query.Listen(snapshot=>{foreach(DocumentChangechangeinsnapshot.GetChanges()){if(change.ChangeType==DocumentChange.Type.Added){Debug.Log(String.Format("New city: {0}",change.Document.Id));}elseif(change.ChangeType==DocumentChange.Type.Modified){Debug.Log(String.Format("Modified city: {0}",change.Document.Id));}elseif(change.ChangeType==DocumentChange.Type.Removed){Debug.Log(String.Format("Removed city: {0}",change.Document.Id));}}});
C#
CollectionReferencecitiesRef=db.Collection("cities");Queryquery=db.Collection("cities").WhereEqualTo("State","CA");FirestoreChangeListenerlistener=query.Listen(snapshot=>{foreach(DocumentChangechangeinsnapshot.Changes){if(change.ChangeType.ToString()=="Added"){Console.WriteLine("New city: {0}",change.Document.Id);}elseif(change.ChangeType.ToString()=="Modified"){Console.WriteLine("Modified city: {0}",change.Document.Id);}elseif(change.ChangeType.ToString()=="Removed"){Console.WriteLine("Removed city: {0}",change.Document.Id);}}});Ruby
query=firestore.col(collection_path).where:state,:==,"CA"added=[]modified=[]removed=[]# Watch the collection query.listener=query.listendo|snapshot|puts"Callback received query snapshot."puts"Current cities in California:"snapshot.changes.eachdo|change|ifchange.added?puts"New city:#{change.doc.document_id}"added <<snapshotelsifchange.modified?puts"Modified city:#{change.doc.document_id}"modified <<snapshotelsifchange.removed?puts"Removed city:#{change.doc.document_id}"removed <<snapshotendendendadded events for all existingdocuments that match the query. This is because you're getting a set of changesthat bring your query snapshot current with the initial state of the query. Thisallows you, for instance, to directly populate your UI from the changes youreceive in the first query snapshot, withoutneeding to add special logic for handling the initial state.The initial state can come from the server directly, or from a localcache. If there is state available in a local cache, the query snapshot willbe initially populated with the cached data, then updated with theserver's data when the client has caught up with the server's state.
Detach a listener
When you are no longer interested in listening to your data, you must detachyour listener so that your event callbacks stop getting called. This allows theclient to stop using bandwidth to receive updates. For example:
Web
import{collection,onSnapshot}from"firebase/firestore";constunsubscribe=onSnapshot(collection(db,"cities"),()=>{// Respond to data// ...});// Later ...// Stop listening to changesunsubscribe();
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.varunsubscribe=db.collection("cities").onSnapshot(()=>{// Respond to data// ...});// Later ...// Stop listening to changesunsubscribe();
Swift
letlistener=db.collection("cities").addSnapshotListener{querySnapshot,errorin// ...}// ...// Stop listening to changeslistener.remove()
Objective-C
id<FIRListenerRegistration>listener=[[self.dbcollectionWithPath:@"cities"]addSnapshotListener:^(FIRQuerySnapshot*snapshot,NSError*error){// ...}];// ...// Stop listening to changes[listenerremove];
Kotlin
valquery=db.collection("cities")valregistration=query.addSnapshotListener{snapshots,e->// ...}// ...// Stop listening to changesregistration.remove()
Java
Queryquery=db.collection("cities");ListenerRegistrationregistration=query.addSnapshotListener(newEventListener<QuerySnapshot>(){// ...});// ...// Stop listening to changesregistration.remove();
Dart
finalcollection=db.collection("cities");finallistener=collection.snapshots().listen((event){// ...});listener.cancel();
Java
Queryquery=db.collection("cities");ListenerRegistrationregistration=query.addSnapshotListener(newEventListener<QuerySnapshot>(){// ...});// ...// Stop listening to changesregistration.remove();Python
# Terminate watch on a documentdoc_watch.unsubscribe()C++
// Add a listenerQueryquery=db->Collection("cities");ListenerRegistrationregistration=query.AddSnapshotListener([](constQuerySnapshot&snapshot,Errorerror,conststd::string&errorMsg){/* ... */});// Stop listening to changesregistration.Remove();
Node.js
constunsub=db.collection('cities').onSnapshot(()=>{});// ...// Stop listening for changesunsub();Go
// Сontext with timeout stops listening to changes.ctx,cancel:=context.WithTimeout(ctx,30*time.Second)defercancel()PHP
// Not supported in the PHP client libraryUnity
listener.Stop();
C#
awaitlistener.StopAsync();Ruby
listener.stopHandle listen errors
A listen may occasionally fail — for example, due to security permissions, or ifyou tried to listen on an invalid query. (Learn more aboutvalid and invalid queries.) To handle thesefailures, you can provide an error callback when you attach your snapshotlistener. After an error, the listener will not receive any more events, andthere is no need to detach your listener.
Web
import{collection,onSnapshot}from"firebase/firestore";constunsubscribe=onSnapshot(collection(db,"cities"),(snapshot)=>{// ...},(error)=>{// ...});
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.db.collection("cities").onSnapshot((snapshot)=>{// ...},(error)=>{// ...});
Swift
db.collection("cities").addSnapshotListener{querySnapshot,errorinifleterror=error{print("Error retreiving collection:\(error)")}}
Objective-C
[[self.dbcollectionWithPath:@"cities"]addSnapshotListener:^(FIRQuerySnapshot*snapshot,NSError*error){if(error!=nil){NSLog(@"Error retreving collection: %@",error);}}];
Kotlin
db.collection("cities").addSnapshotListener{snapshots,e->if(e!=null){Log.w(TAG,"listen:error",e)return@addSnapshotListener}for(dcinsnapshots!!.documentChanges){if(dc.type==DocumentChange.Type.ADDED){Log.d(TAG,"New city:${dc.document.data}")}}}
Java
db.collection("cities").addSnapshotListener(newEventListener<QuerySnapshot>(){@OverridepublicvoidonEvent(@NullableQuerySnapshotsnapshots,@NullableFirebaseFirestoreExceptione){if(e!=null){Log.w(TAG,"listen:error",e);return;}for(DocumentChangedc:snapshots.getDocumentChanges()){if(dc.getType()==Type.ADDED){Log.d(TAG,"New city: "+dc.getDocument().getData());}}}});
Dart
finaldocRef=db.collection("cities");docRef.snapshots().listen((event)=>print("listener attached"),onError:(error)=>print("Listen failed:$error"),);
Java
db.collection("cities").addSnapshotListener(newEventListener<QuerySnapshot>(){@OverridepublicvoidonEvent(@NullableQuerySnapshotsnapshots,@NullableFirestoreExceptione){if(e!=null){System.err.println("Listen failed: "+e);return;}for(DocumentChangedc:snapshots.getDocumentChanges()){if(dc.getType()==Type.ADDED){System.out.println("New city: "+dc.getDocument().getData());}}}});Python
//Snippetcomingsoon
C++
// Snippet coming soon.Node.js
db.collection('cities').onSnapshot((snapshot)=>{//...},(error)=>{//...});Go
import("context""fmt""io""time""cloud.google.com/go/firestore""google.golang.org/grpc/codes""google.golang.org/grpc/status")// listenErrors demonstrates how to handle listening errors.funclistenErrors(ctxcontext.Context,wio.Writer,projectID,collectionstring)error{// projectID := "project-id"ctx,cancel:=context.WithTimeout(ctx,30*time.Second)defercancel()client,err:=firestore.NewClient(ctx,projectID)iferr!=nil{returnfmt.Errorf("firestore.NewClient: %w",err)}deferclient.Close()it:=client.Collection(collection).Snapshots(ctx)for{snap,err:=it.Next()// Canceled will be returned when ctx is cancelled and DeadlineExceeded will// be returned when ctx reaches its deadline.ife:=status.Code(err);e==codes.Canceled||e==codes.DeadlineExceeded{returnnil}iferr!=nil{returnfmt.Errorf("Snapshots.Next: %w",err)}ifsnap!=nil{for_,change:=rangesnap.Changes{ifchange.Kind==firestore.DocumentAdded{fmt.Fprintf(w,"New city: %v\n",change.Doc.Data())}}}}}PHP
// Not supported in the PHP client libraryUnity
ListenerRegistrationregistration=db.Collection("cities").Listen(querySnapshot=>{// ...});registration.ListenerTask.ContinueWithOnMainThread(listenerTask=>{if(listenerTask.IsFaulted){Debug.LogError($"Listen failed: {listenerTask.Exception}");// ...// Handle the listener error.// ...}});
C#
// Snippet coming soonRuby
listener=firestore.col(collection_path).listendo|snapshot|snapshot.changes.eachdo|change|puts"New city:#{change.doc.document_id}"ifchange.added?endend# Register to be notified when unhandled errors occur.listener.on_errordo|error|puts"Listen failed:#{error.message}"endPricing
Pricing for realtime listener operations depends on the edition of the database.See the following:
What's next
- Combine listeners with simple and compound queries.
- Order and limit the documents retrieved.
- Understand billing for listeners.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-18 UTC.