# How to Delete a Document in Firestore

- Tool: Firebase
- Difficulty: Intermediate
- Time required: 10-15 min
- Compatibility: Firebase JS SDK v9+, Firestore (all plans)
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

```
import { getFirestore, doc, deleteDoc } from 'firebase/firestore';

const db = getFirestore();

// Delete a single document by ID
async function deleteTodo(todoId: string): Promise<void> {
  const todoRef = doc(db, 'todos', todoId);
  await deleteDoc(todoRef);
  console.log('Document deleted:', todoId);
}
```

**Expected result:** The document is removed from Firestore. Subsequent reads return a snapshot where exists() is false.

### 2. 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.

```
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /todos/{todoId} {
      // Only the document owner can delete
      allow delete: if request.auth != null
        && request.auth.uid == resource.data.userId;

      // Read and create rules
      allow read: if request.auth != null
        && request.auth.uid == resource.data.userId;
      allow create: if request.auth != null
        && request.auth.uid == request.resource.data.userId;
    }
  }
}
```

**Expected result:** Authenticated users can delete documents where the userId field matches their auth UID. Unauthorized delete attempts throw a permission-denied error.

### 3. 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.

```
import { doc, updateDoc, deleteField } from 'firebase/firestore';

const db = getFirestore();

// Remove the 'completedAt' field from a todo document
async function removeCompletedTimestamp(todoId: string): Promise<void> {
  const todoRef = doc(db, 'todos', todoId);
  await updateDoc(todoRef, {
    completedAt: deleteField()
  });
  console.log('Field removed from document:', todoId);
}
```

**Expected result:** The completedAt field is removed from the document. All other fields remain unchanged.

### 4. 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.

```
import { getFirestore, doc, writeBatch, collection, getDocs, query, where } from 'firebase/firestore';

const db = getFirestore();

// Delete all completed todos for the current user
async function deleteCompletedTodos(userId: string): Promise<number> {
  const q = query(
    collection(db, 'todos'),
    where('userId', '==', userId),
    where('completed', '==', true)
  );

  const snapshot = await getDocs(q);
  const batch = writeBatch(db);

  snapshot.docs.forEach((docSnap) => {
    batch.delete(docSnap.ref);
  });

  await batch.commit();
  return snapshot.size;
}
```

**Expected result:** All matching documents are deleted atomically. The function returns the count of deleted documents.

### 5. 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.

```
import { collection, getDocs, writeBatch, doc } from 'firebase/firestore';

const db = getFirestore();

// Delete a document and its subcollection (client-side)
async function deleteProjectWithTasks(projectId: string): Promise<void> {
  // First delete all subcollection documents
  const tasksRef = collection(db, 'projects', projectId, 'tasks');
  const tasksSnapshot = await getDocs(tasksRef);

  const batch = writeBatch(db);
  tasksSnapshot.docs.forEach((taskDoc) => {
    batch.delete(taskDoc.ref);
  });

  // Then delete the parent document
  batch.delete(doc(db, 'projects', projectId));
  await batch.commit();
}
```

**Expected result:** Both the parent document and all its subcollection documents are deleted in a single atomic batch operation.

## Complete code example

File: `firestore-delete.ts`

```typescript
import { initializeApp } from 'firebase/app';
import {
  getFirestore,
  doc,
  deleteDoc,
  updateDoc,
  deleteField,
  writeBatch,
  collection,
  getDocs,
  query,
  where
} from 'firebase/firestore';

const app = initializeApp({
  // Your Firebase config
});
const db = getFirestore(app);

// Delete a single document
export async function deleteTodo(todoId: string): Promise<void> {
  const todoRef = doc(db, 'todos', todoId);
  await deleteDoc(todoRef);
}

// Remove a field from a document
export async function removeField(
  todoId: string,
  fieldName: string
): Promise<void> {
  const todoRef = doc(db, 'todos', todoId);
  await updateDoc(todoRef, { [fieldName]: deleteField() });
}

// Batch delete all completed todos for a user
export async function deleteCompletedTodos(
  userId: string
): Promise<number> {
  const q = query(
    collection(db, 'todos'),
    where('userId', '==', userId),
    where('completed', '==', true)
  );
  const snapshot = await getDocs(q);

  if (snapshot.empty) return 0;

  const batch = writeBatch(db);
  snapshot.docs.forEach((d) => batch.delete(d.ref));
  await batch.commit();
  return snapshot.size;
}

// Delete parent document and its subcollection
export async function deleteWithSubcollection(
  parentCollection: string,
  parentId: string,
  subCollection: string
): Promise<void> {
  const subRef = collection(db, parentCollection, parentId, subCollection);
  const subSnapshot = await getDocs(subRef);

  const batch = writeBatch(db);
  subSnapshot.docs.forEach((d) => batch.delete(d.ref));
  batch.delete(doc(db, parentCollection, parentId));
  await batch.commit();
}
```

## Common mistakes

- **Assuming deleteDoc also deletes subcollections** — undefined Fix: 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.
- **Not adding a specific 'allow delete' rule in security rules** — undefined Fix: 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.
- **Trying to delete more than 500 documents in a single batch** — undefined Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-delete-a-document-in-firestore
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-delete-a-document-in-firestore
