diff --git a/docs/pages/api-reference/liveblocks-react.mdx b/docs/pages/api-reference/liveblocks-react.mdx
index 72dd1cc01f..bcb6d7ab73 100644
--- a/docs/pages/api-reference/liveblocks-react.mdx
+++ b/docs/pages/api-reference/liveblocks-react.mdx
@@ -3776,14 +3776,43 @@ Returns a function that uploads a file to the current room. The function
resolves to a [`LiveFile`](/docs/api-reference/liveblocks-client#LiveFile) that
can be stored in Storage.
+Because [`useMutation`](/docs/api-reference/liveblocks-react#useMutation)
+[doesn’t support async callbacks](/docs/api-reference/liveblocks-react#useMutation-async),
+await the upload outside of the mutation, then pass the resulting `LiveFile`
+into it.
+
```tsx
+import { useCallback } from "react";
import { useMutation, useUploadFile } from "@liveblocks/react/suspense";
+import type { LiveFile } from "@liveblocks/client";
-const uploadFile = useUploadFile();
+function UploadFile() {
+ const uploadFile = useUploadFile();
-const setFile = useMutation(async ({ storage }, file: File) => {
- storage.set("file", await uploadFile(file));
-}, []);
+ const setFile = useMutation(({ storage }, file: LiveFile) => {
+ storage.set("file", file);
+ }, []);
+
+ const handleFile = useCallback(
+ async (file: File) => {
+ const liveFile = await uploadFile(file);
+ setFile(liveFile);
+ },
+ [uploadFile, setFile]
+ );
+
+ return (
+ {
+ const file = e.currentTarget.files?.[0];
+ if (file) {
+ handleFile(file);
+ }
+ }}
+ />
+ );
+}
```
The function accepts the same arguments and returns the same result as
@@ -4092,6 +4121,32 @@ return ;
Mutations are automatically batched, so when using `useMutation` there’s no need
to use `useBatch`, or call `room.batch()` manually.
+#### Doesn’t work with async functions [#useMutation-async]
+
+Because mutations are batched with
+[`room.batch`](/docs/api-reference/liveblocks-client#Room.batch), which
+[cannot take an `async` function](/docs/api-reference/liveblocks-client#Doesn't-work-with-async-functions),
+the mutation function passed to `useMutation` cannot be `async` either.
+
+```tsx
+// ❌ Won’t work
+const updateFill = useMutation(async ({ storage }) => {
+ const fill = await fetchColor();
+ storage.get("shapes").get("circle1").set("fill", fill);
+}, []);
+
+// ✅ Will work
+const updateFill = useMutation(({ storage }, fill: string) => {
+ storage.get("shapes").get("circle1").set("fill", fill);
+}, []);
+
+// …with the async work happening outside of the mutation
+const handleClick = useCallback(async () => {
+ const fill = await fetchColor();
+ updateFill(fill);
+}, [updateFill]);
+```
+
#### ESLint rule [#useMutation-lint-rule] [@keywords=["exhaustive-deps", "additionalHooks", "eslint-plugin-react-hooks"]]
If you are using ESLint in your project, and are using