diff --git a/packages/react/src/firestore/index.ts b/packages/react/src/firestore/index.ts
index efee1267..ec75c7b1 100644
--- a/packages/react/src/firestore/index.ts
+++ b/packages/react/src/firestore/index.ts
@@ -4,7 +4,7 @@ export { useDisableNetworkMutation } from "./useDisableNetworkMutation";
// useEnableNetworkMutation
export { useWaitForPendingWritesQuery } from "./useWaitForPendingWritesQuery";
export { useRunTransactionMutation } from "./useRunTransactionMutation";
-// useWriteBatchCommitMutation (WriteBatch)
+export { useWriteBatchCommitMutation } from "./useWriteBatchCommitMutation";
export { useDocumentQuery } from "./useDocumentQuery";
export { useCollectionQuery } from "./useCollectionQuery";
export { useGetAggregateFromServerQuery } from "./useGetAggregateFromServerQuery";
diff --git a/packages/react/src/firestore/useWriteBatchCommitMutation.test.tsx b/packages/react/src/firestore/useWriteBatchCommitMutation.test.tsx
new file mode 100644
index 00000000..cfbf9fb4
--- /dev/null
+++ b/packages/react/src/firestore/useWriteBatchCommitMutation.test.tsx
@@ -0,0 +1,120 @@
+import React from "react";
+import { describe, expect, test, beforeEach } from "vitest";
+import { renderHook, act, waitFor } from "@testing-library/react";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { firestore, wipeFirestore } from "~/testing-utils";
+import { useWriteBatchCommitMutation } from "./useWriteBatchCommitMutation";
+import { doc, getDoc, setDoc, writeBatch } from "firebase/firestore";
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: { retry: false },
+ mutations: { retry: false },
+ },
+});
+
+const wrapper = ({ children }: { children: React.ReactNode }) => (
+ {children}
+);
+
+describe("useWriteBatchCommitMutation", () => {
+ beforeEach(async () => {
+ queryClient.clear();
+ await wipeFirestore();
+ });
+
+ test("successfully creates and commits a write batch", async () => {
+ const docRef1 = doc(firestore, "tests", "doc1");
+ const docRef2 = doc(firestore, "tests", "doc2");
+
+ const { result } = renderHook(() => useWriteBatchCommitMutation(), {
+ wrapper,
+ });
+
+ await act(async () => {
+ const batch = writeBatch(firestore);
+ batch.set(docRef1, { value: "test1" });
+ batch.set(docRef2, { value: "test2" });
+ await result.current.mutate(batch);
+ });
+
+ const doc1Snapshot = await getDoc(docRef1);
+ const doc2Snapshot = await getDoc(docRef2);
+
+ await waitFor(async () => {
+ expect(doc1Snapshot.exists()).toBe(true);
+ expect(doc2Snapshot.exists()).toBe(true);
+
+ expect(doc1Snapshot.data()).toEqual({ value: "test1" });
+ expect(doc2Snapshot.data()).toEqual({ value: "test2" });
+ });
+ });
+
+ test("handles multiple operations in a single batch", async () => {
+ const docRef1 = doc(firestore, "tests", "doc1");
+ const docRef2 = doc(firestore, "tests", "doc2");
+ const docRef3 = doc(firestore, "tests", "doc3");
+
+ const { result } = renderHook(() => useWriteBatchCommitMutation(), {
+ wrapper,
+ });
+
+ await setDoc(docRef1, { value: "initial1" });
+ await setDoc(docRef2, { value: "initial2" });
+
+ await act(async () => {
+ const batch = writeBatch(firestore);
+ batch.update(docRef1, { value: "updated1" });
+ batch.update(docRef1, { value: "updated1" });
+ batch.delete(docRef2);
+ batch.set(docRef3, { value: "new3" });
+ await result.current.mutate(batch);
+ });
+
+ const doc1Snapshot = await getDoc(docRef1);
+ const doc2Snapshot = await getDoc(docRef2);
+ const doc3Snapshot = await getDoc(docRef3);
+
+ await waitFor(async () => {
+ expect(doc1Snapshot.data()).toEqual({ value: "updated1" });
+ expect(doc2Snapshot.exists()).toBe(false);
+ expect(doc3Snapshot.data()).toEqual({ value: "new3" });
+ });
+ });
+
+ test("successfully creates and commits a write batch with nested fields", async () => {
+ const docRef1 = doc(firestore, "tests", "doc1");
+ const docRef2 = doc(firestore, "tests", "doc2");
+
+ await setDoc(docRef1, {
+ fieldToUpdate: { nestedField: "value" },
+ });
+
+ const { result } = renderHook(() => useWriteBatchCommitMutation(), {
+ wrapper,
+ });
+
+ await act(async () => {
+ const batch = writeBatch(firestore);
+
+ batch.set(docRef2, { value: "test2" });
+ batch.update(docRef1, { "fieldToUpdate.nestedField": "newValue" });
+ await result.current.mutate(batch);
+ });
+
+ const doc1Snapshot = await getDoc(docRef1);
+ const doc2Snapshot = await getDoc(docRef2);
+
+ await waitFor(async () => {
+ expect(doc1Snapshot.exists()).toBe(true);
+ expect(doc2Snapshot.exists()).toBe(true);
+
+ expect(doc1Snapshot.data()).toEqual({
+ fieldToUpdate: { nestedField: "newValue" },
+ });
+ expect(doc2Snapshot.data()).toEqual({
+ value: "test2",
+ });
+ });
+ });
+});
diff --git a/packages/react/src/firestore/useWriteBatchCommitMutation.ts b/packages/react/src/firestore/useWriteBatchCommitMutation.ts
new file mode 100644
index 00000000..4719b51c
--- /dev/null
+++ b/packages/react/src/firestore/useWriteBatchCommitMutation.ts
@@ -0,0 +1,16 @@
+import { useMutation, type UseMutationOptions } from "@tanstack/react-query";
+import { type FirestoreError, type WriteBatch } from "firebase/firestore";
+
+type FirestoreUseMutationOptions = Omit<
+ UseMutationOptions,
+ "mutationFn"
+>;
+
+export function useWriteBatchCommitMutation(
+ options?: FirestoreUseMutationOptions
+) {
+ return useMutation({
+ ...options,
+ mutationFn: (batch: WriteBatch) => batch.commit(),
+ });
+}