To delete a document in Firestore, use deleteDoc() with a document reference from the modular SDK v9+. Pass the result of doc(db, 'collection', 'documentId') to deleteDoc(). Deleting a document does not delete its subcollections — you must delete subcollection documents separately using a recursive approach or a Cloud Function. Always write security rules that grant delete permission only to authorized users.
Deleting Documents in Firestore
Firestore's modular SDK provides the deleteDoc() function for removing documents from your database. This tutorial covers single document deletion, batch deletes for removing multiple documents at once, handling subcollections that survive parent document deletion, and the security rules required to authorize delete operations from your client app.
Prerequisites
- A Firebase project with Firestore enabled
- Firebase JS SDK v9+ installed in your project
- Firestore initialized with initializeApp and getFirestore
- Security rules that grant delete permission (covered in this tutorial)
Step-by-step guide
Delete a single document with deleteDoc
Delete a single document with deleteDoc
Import deleteDoc and doc from firebase/firestore. Create a document reference by passing the Firestore instance, collection name, and document ID to doc(). Then call deleteDoc() with that reference. The function returns a Promise that resolves when the delete is committed to the server. If the document does not exist, deleteDoc succeeds silently without throwing an error.
1import { getFirestore, doc, deleteDoc } from 'firebase/firestore';23const db = getFirestore();45// Delete a single document by ID6async function deleteTodo(todoId: string): Promise<void> {7 const todoRef = doc(db, 'todos', todoId);8 await deleteDoc(todoRef);9 console.log('Document deleted:', todoId);10}Expected result: The document is removed from Firestore. Subsequent reads return a snapshot where exists() is false.
Write security rules to allow delete operations
Write security rules to allow delete operations
Firestore security rules must explicitly allow delete operations. By default, if you have allow write rules, they cover create, update, and delete. For finer control, use allow delete as a separate permission. A common pattern is to let users delete only their own documents by checking that request.auth.uid matches the document's authorId or userId field.
1rules_version = '2';2service cloud.firestore {3 match /databases/{database}/documents {4 match /todos/{todoId} {5 // Only the document owner can delete6 allow delete: if request.auth != null7 && request.auth.uid == resource.data.userId;89 // Read and create rules10 allow read: if request.auth != null11 && request.auth.uid == resource.data.userId;12 allow create: if request.auth != null13 && request.auth.uid == request.resource.data.userId;14 }15 }16}Expected result: Authenticated users can delete documents where the userId field matches their auth UID. Unauthorized delete attempts throw a permission-denied error.
Delete specific fields from a document instead of the entire document
Delete specific fields from a document instead of the entire document
Sometimes you want to remove a field from a document rather than deleting the whole document. Use updateDoc with deleteField() as the value. This removes the specified field while keeping the rest of the document intact.
1import { doc, updateDoc, deleteField } from 'firebase/firestore';23const db = getFirestore();45// Remove the 'completedAt' field from a todo document6async function removeCompletedTimestamp(todoId: string): Promise<void> {7 const todoRef = doc(db, 'todos', todoId);8 await updateDoc(todoRef, {9 completedAt: deleteField()10 });11 console.log('Field removed from document:', todoId);12}Expected result: The completedAt field is removed from the document. All other fields remain unchanged.
Delete multiple documents in a batch
Delete multiple documents in a batch
Use writeBatch to delete multiple documents atomically. All deletes in a batch either succeed together or fail together. This is useful for cleaning up related data across a collection. Batches are limited to 500 operations, so for larger deletions, split them into multiple batches.
1import { getFirestore, doc, writeBatch, collection, getDocs, query, where } from 'firebase/firestore';23const db = getFirestore();45// Delete all completed todos for the current user6async function deleteCompletedTodos(userId: string): Promise<number> {7 const q = query(8 collection(db, 'todos'),9 where('userId', '==', userId),10 where('completed', '==', true)11 );1213 const snapshot = await getDocs(q);14 const batch = writeBatch(db);1516 snapshot.docs.forEach((docSnap) => {17 batch.delete(docSnap.ref);18 });1920 await batch.commit();21 return snapshot.size;22}Expected result: All matching documents are deleted atomically. The function returns the count of deleted documents.
Handle subcollection cleanup
Handle subcollection cleanup
Deleting a parent document does not delete its subcollections. Subcollection documents continue to exist as orphaned data. To fully clean up, you must delete subcollection documents explicitly. From the client side, query the subcollection and delete each document. For production apps, use a Cloud Function triggered on document deletion to handle recursive cleanup server-side with the Admin SDK.
1import { collection, getDocs, writeBatch, doc } from 'firebase/firestore';23const db = getFirestore();45// Delete a document and its subcollection (client-side)6async function deleteProjectWithTasks(projectId: string): Promise<void> {7 // First delete all subcollection documents8 const tasksRef = collection(db, 'projects', projectId, 'tasks');9 const tasksSnapshot = await getDocs(tasksRef);1011 const batch = writeBatch(db);12 tasksSnapshot.docs.forEach((taskDoc) => {13 batch.delete(taskDoc.ref);14 });1516 // Then delete the parent document17 batch.delete(doc(db, 'projects', projectId));18 await batch.commit();19}Expected result: Both the parent document and all its subcollection documents are deleted in a single atomic batch operation.
Complete working example
1import { initializeApp } from 'firebase/app';2import {3 getFirestore,4 doc,5 deleteDoc,6 updateDoc,7 deleteField,8 writeBatch,9 collection,10 getDocs,11 query,12 where13} from 'firebase/firestore';1415const app = initializeApp({16 // Your Firebase config17});18const db = getFirestore(app);1920// Delete a single document21export async function deleteTodo(todoId: string): Promise<void> {22 const todoRef = doc(db, 'todos', todoId);23 await deleteDoc(todoRef);24}2526// Remove a field from a document27export async function removeField(28 todoId: string,29 fieldName: string30): Promise<void> {31 const todoRef = doc(db, 'todos', todoId);32 await updateDoc(todoRef, { [fieldName]: deleteField() });33}3435// Batch delete all completed todos for a user36export async function deleteCompletedTodos(37 userId: string38): Promise<number> {39 const q = query(40 collection(db, 'todos'),41 where('userId', '==', userId),42 where('completed', '==', true)43 );44 const snapshot = await getDocs(q);4546 if (snapshot.empty) return 0;4748 const batch = writeBatch(db);49 snapshot.docs.forEach((d) => batch.delete(d.ref));50 await batch.commit();51 return snapshot.size;52}5354// Delete parent document and its subcollection55export async function deleteWithSubcollection(56 parentCollection: string,57 parentId: string,58 subCollection: string59): Promise<void> {60 const subRef = collection(db, parentCollection, parentId, subCollection);61 const subSnapshot = await getDocs(subRef);6263 const batch = writeBatch(db);64 subSnapshot.docs.forEach((d) => batch.delete(d.ref));65 batch.delete(doc(db, parentCollection, parentId));66 await batch.commit();67}Common mistakes when deleting a Document in Firestore
Why it's a problem: Assuming deleteDoc also deletes subcollections
How to avoid: Subcollections survive parent document deletion. You must query and delete subcollection documents separately, either from the client or via a Cloud Function using the Admin SDK's recursiveDelete method.
Why it's a problem: Not adding a specific 'allow delete' rule in security rules
How to avoid: If you only have 'allow create' and 'allow update' rules, delete operations will be denied. Add 'allow delete: if ...' as a separate permission, or use the broader 'allow write' which covers create, update, and delete.
Why it's a problem: Trying to delete more than 500 documents in a single batch
How to avoid: Firestore batches are limited to 500 operations. For larger deletions, split documents into groups of 500 and commit each batch sequentially.
Best practices
- Always verify user authorization in security rules before allowing deletes — check request.auth.uid against the document owner
- Use writeBatch for deleting multiple documents to ensure atomicity
- Implement server-side cleanup with Cloud Functions for subcollection data when deleting parent documents
- Consider soft deletes (adding a deletedAt timestamp) instead of hard deletes when you need audit trails
- Limit batch sizes to 500 operations and process larger deletions in sequential batches
- Test delete security rules in the Emulator Suite before deploying to production
- Log delete operations with structured data for debugging and audit purposes
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Show me how to delete a document in Firestore using the modular SDK v9+. Include examples for single delete, batch delete of multiple documents, handling subcollection cleanup, and the Firestore security rules needed to allow delete operations for document owners only.
Write Firestore delete utility functions using the modular SDK v9+: single document deletion with deleteDoc, batch deletion of multiple documents matching a query, subcollection cleanup before parent deletion, and field removal with deleteField. Include security rules for owner-only delete permission.
Frequently asked questions
Does deleting a document delete its subcollections?
No. Deleting a parent document leaves its subcollections intact as orphaned data. You must delete subcollection documents separately, either from client code or using a Cloud Function with the Admin SDK's recursiveDelete method.
Does deleteDoc throw an error if the document does not exist?
No. deleteDoc succeeds silently even if the document does not exist. If you need to verify the document existed, read it with getDoc before deleting.
How do I delete all documents in a collection?
There is no single API call to delete an entire collection. Query all documents with getDocs, then delete them in batches of 500 using writeBatch. For large collections, use the Firebase CLI command firebase firestore:delete --all-collections or the Admin SDK's recursiveDelete.
Can I undo a Firestore document deletion?
No. Firestore deletions are permanent and cannot be undone through the API. Enable point-in-time recovery (PITR) on your database for disaster recovery, or implement soft deletes by adding a deletedAt field instead of actually removing documents.
What is the difference between deleteDoc and deleteField?
deleteDoc removes an entire document from the collection. deleteField is used inside updateDoc to remove a specific field from a document while keeping all other fields intact.
Can RapidDev help with complex Firestore data cleanup workflows?
Yes, RapidDev can build Cloud Functions for recursive subcollection deletion, implement soft-delete patterns with scheduled cleanup, and design security rules that safely authorize delete operations for your application.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation