Today, we’re introducing [`LiveFile`][], a new way to upload and share files
directly within our sync engine, [Liveblocks Storage][].

## Uploading files

In collaborative applications, such as canvases and text editors, users often
need to share files as part of their work. The simplest example is inserting an
image into a whiteboard, which everyone in the room should see immediately.

<Figure>
  <MuxVideo
    playbackId="6H2gch01700S8XzfcEu4O02ZsQsaTD2gJMCHcnZ3QSXxaw"
    alt="Uploading an image to a collaborative canvas with LiveFile"
    static={true}
  />
</Figure>

Another example is pasting media or files into a collaborative text editor.

<Figure caption={<></>}>
  <MuxVideo
    playbackId="n57kc01y02mj02027Cm9vfvtMku4xfgpdOVuX6aWh64ZV1Q"
    alt="Uploading a file to a collaborative text editor with LiveFile"
    static={true}
  />
</Figure>

Previously, [Liveblocks Storage][] could only store JSON-like data, and you
could not upload files—but today we’re introducing a new way to upload and share
files as part of your Storage document.

## Introducing LiveFile

[`LiveFile`][] is a new Storage data type that allows you to upload and share
files as part of your Storage document. This means you no longer need to upload
files to a third-party service to share them with other users in your room.
Plus, when you update files, other users in the room will see the changes live.

<Figure>
  <MuxVideo
    playbackId="Qjgmwox46wdBD6J01402HGOZUTtkxi00deY9AOnHj01WSDw"
    alt="Sharing an uploaded image with other users in a Liveblocks room"
    static={true}
  />
</Figure>

[`LiveFile`][] supports any file type, not only images. Documents, `.zip`
archives, presentations, and more can be inserted into your app and made
available with a download button.

<Figure caption={<></>}>
  <MuxVideo
    playbackId="oD2zZpv3Vxs1WhHLeonhLd02s1EPzuYISBlpmswLUC02A"
    alt="Downloading a file"
    static={true}
  />
</Figure>

## Start uploading files [#start-uploading-files]

To get started, use
[`useUploadFile`](/docs/api-reference/liveblocks-react#useUploadFile) to upload
a file, then attach the returned `LiveFile` to your Storage document inside a
mutation.

```tsx
import { useMutation, useUploadFile } from "@liveblocks/react/suspense";

function UploadFile() {
  const uploadFile = useUploadFile();

  const addFileToStorage = useMutation(({ storage }, liveFile) => {
    storage.set("myFile", liveFile);
  }, []);

  const handleFileChange = useCallback(async (file) => {
    const liveFile = await uploadFile(file);
    addFileToStorage(liveFile);
  }, []);

  return (
    <label>
      <input
        type="file"
        onChange={(e) => handleFileChange(e.currentTarget.files[0])}
      />
      ⬆️ Upload file
    </label>
  );
}
```

To access the file URL in your application, first read the `LiveFile` from
Storage with
[`useStorage`](/docs/api-reference/liveblocks-react-suspense#useStorage), then
get its URL with
[`useFileUrl`](/docs/api-reference/liveblocks-react-suspense#useFileUrl). In the
example above, the file is stored under the `"myFile"` property.

```tsx
import { useFileUrl, useStorage } from "@liveblocks/react/suspense";

function DownloadFile() {
  const myFile = useStorage((root) => root.myFile);
  const fileUrl = useFileUrl(myFile);

  return (
    <a href={fileUrl} download>
      ⬇️ Download file
    </a>
  );
}
```

For more details, explore the
[`LiveFile` API reference](/docs/api-reference/liveblocks-client#LiveFile).

## Server-side uploads [#server-side-uploads]

You can also upload files from the server with
[`liveblocks.uploadFile`](/docs/api-reference/liveblocks-node#upload-file), then
add them to your Storage document with
[`mutateStorage`](/docs/api-reference/liveblocks-node#mutate-storage).

```ts
import { Liveblocks } from "@liveblocks/node";

const liveblocks = new Liveblocks({
  secret: "{{SECRET_KEY}}",
});

const roomId = "my-room-id";
const file = new File(["Hello world"], "my-file.txt");

const liveFile = await liveblocks.uploadFile({
  roomId,
  file,
});

await liveblocks.mutateStorage(roomId, ({ root }) => {
  root.set("myFile", liveFile);
});
```

This is particularly useful in combination with agentic workflows that perform
background processing on files. An example is using
[AI SDK](https://ai-sdk.dev/) to generate images, which will be shared with
connected users in your application.

```ts
import { Liveblocks } from "@liveblocks/node";
import { openai } from "@ai-sdk/openai";
import { generateImage } from "ai";

const liveblocks = new Liveblocks({
  secret: "{{SECRET_KEY}}",
});

const roomId = "my-room-id";

// +++
const {
  images: [image],
} = await generateImage({
  model: openai.image("gpt-image-1"),
  prompt: "A futuristic cityscape at sunset",
  n: 1,
  size: "1024x1024",
});
// +++

// +++
const file = new File([image.uint8Array], "my-generated-image.png", {
  type: image.mediaType,
});
// +++

const liveFile = await liveblocks.uploadFile({
  roomId,
  file,
});

await liveblocks.mutateStorage(roomId, ({ root }) => {
  root.set("myGeneratedImage", liveFile);
});
```

A [REST API](/docs/api-reference/rest-api-endpoints#upload-file) is also
available for server-side uploads.

## Whiteboard example

We’ve updated our [Tldraw](https://www.tldraw.com/)-powered collaborative
whiteboard to use [`LiveFile`][] for uploads, enabling media sharing without a
third-party service. There’s a live demo below—try uploading an image or video
to the canvas.

<Figure>
  <Embed
    zoom={0.87}
    src="https://nextjs-tldraw-whiteboard-storage.liveblocks.app/"
    className="aspect-16/10 w-full bg-white"
  />
</Figure>

If you’d like to copy this whiteboard into your application, download the
[Tldraw Whiteboard](/examples/tldraw-whiteboard/nextjs-tldraw-whiteboard-storage)
repository from our example gallery.

## Get started now [#get-started-now]

[`LiveFile`][] is available today in public beta. To try it, update your
Liveblocks packages with the following command.

```bash
npx liveblocks upgrade
```

If you’re currently using Liveblocks 3.17 or below, follow our
[upgrade guides](/docs/platform/upgrading) before updating.

## Contributors

<Contributors gitHubUsernames={["ofoucherot", "marcbouchenoire"]} />

[`LiveFile`]: /docs/api-reference/liveblocks-client#LiveFile
[Liveblocks Storage]:
  /docs/collaboration-features/multiplayer/sync-engine/liveblocks-storage