> ## Documentation Index
> Fetch the complete documentation index at: https://docs.deep.space/llms.txt
> Use this file to discover all available pages before exploring further.

# Files reference

> `useR2Files` and file display helpers.

The `useR2Files` hook handles uploads, listings, deletions, and signed URLs against the app's R2 bucket. All operations route through the platform's file gateway, so end users never touch raw R2 credentials. For patterns and worked examples, see the [file uploads guide](/guides/file-uploads).

```ts theme={null}
import { useR2Files, isImageFile, formatFileSize } from 'deepspace'
import type { R2FileInfo, R2Scope } from 'deepspace'
```

## `useR2Files(options?)`

```ts theme={null}
type R2UploadResult = {
  success: boolean
  key?: string
  url?: string
  name?: string
  error?: string
}

function useR2Files(options?: R2Scope): {
  upload:       (file: File | Blob, name?: string) => Promise<R2UploadResult>
  uploadBase64: (base64Data: string, name: string, mimeType?: string) => Promise<R2UploadResult>
  deleteFile:   (fileOrKey: R2FileInfo | string) => Promise<{ success: boolean; error?: string }>
  downloadFile: (fileOrKey: R2FileInfo | string, fileName?: string) => Promise<{ success: boolean; error?: string }>
  readFile:     (fileOrKey: R2FileInfo | string) => Promise<Response>
  list:         (prefix?: string) => Promise<R2FileInfo[]>
  getUrl:       (fileOrKey: R2FileInfo | string) => string
  isUploading:  boolean
}
```

`options` is the `R2Scope` itself - pass `{ scope: 'self' }` (or omit entirely; `'self'` is the only valid scope). Every method that takes a file accepts either an `R2FileInfo` object from `list()` or a raw key string.

<Tabs>
  <Tab title="Upload">
    `upload` accepts a `File` (from `<input type="file">` or a drag-drop event) or a `Blob` and an optional display name. Returns `R2UploadResult` - check `success` and read the `key` field.

    ```tsx theme={null}
    const { upload, isUploading } = useR2Files()

    async function onFileChange(e: React.ChangeEvent<HTMLInputElement>) {
      const file = e.target.files?.[0]
      if (!file) return
      const result = await upload(file, file.name)
      if (!result.success) return console.error(result.error)
      console.log('uploaded:', result.key)
    }
    ```
  </Tab>

  <Tab title="Upload (base64)">
    Use when you have data as a Base64 string - for example, from `<canvas>` `toDataURL()`. `name` is required; `mimeType` is optional.

    ```tsx theme={null}
    const { uploadBase64 } = useR2Files()

    const canvas = canvasRef.current!
    const dataUrl = canvas.toDataURL('image/png')
    const base64 = dataUrl.split(',')[1]

    const result = await uploadBase64(base64, 'drawing.png', 'image/png')
    ```
  </Tab>

  <Tab title="List">
    `list()` is an async function - call it and store the result in component state rather than expecting a reactive array. Pass a sub-prefix to filter.

    ```tsx theme={null}
    import { useState, useEffect } from 'react'

    const { list } = useR2Files()
    const [files, setFiles] = useState<R2FileInfo[]>([])

    async function refresh() {
      setFiles(await list())
    }

    useEffect(() => { refresh() }, [])
    ```
  </Tab>

  <Tab title="Delete">
    Removes the file from R2 and broadcasts the change. Accepts either the full `R2FileInfo` from `list()` or a raw key string. There is no recycle bin - deletes are immediate and irreversible.

    ```tsx theme={null}
    const { deleteFile } = useR2Files()

    await deleteFile(file)        // R2FileInfo from list()
    await deleteFile('reports/q1.pdf') // or a raw key
    ```
  </Tab>

  <Tab title="Read / download">
    `getUrl()` returns a plain URL (no auth attached - only usable for unauthenticated reads). `downloadFile()` triggers a browser-side blob download and returns `{ success, error? }`. `readFile()` returns the raw `Response` so you can call `.text()`, `.blob()`, `.arrayBuffer()`, `.json()`, etc.

    ```tsx theme={null}
    const { getUrl, downloadFile, readFile } = useR2Files()

    <img src={getUrl(file)} alt="" />

    await downloadFile(file)                       // triggers Save As…
    await downloadFile('reports/q1.pdf', 'q1.pdf') // explicit filename

    const response = await readFile(file)
    const text = await response.text()
    ```
  </Tab>
</Tabs>

`list()` is an async function - call it and store the result in component state rather than reading a reactive array.

## `R2FileInfo`

```ts theme={null}
type R2FileInfo = {
  key: string
  size: number
  uploaded: string
  url: string
  originalName?: string
  uploadedBy?: string
}
```

<Note>
  There is no `mimeType` / `contentType` field on `R2FileInfo`. Capture the MIME type at upload time and store it in a sidecar [collection](/concepts/data-model) if you need it later. See [storing metadata](/guides/file-uploads#storing-metadata).
</Note>

## Scoping - `R2Scope`

```ts theme={null}
type R2Scope = { scope?: 'self' }
```

The hook is always scoped to the current app (`scope: 'self'`) - the platform derives the bucket prefix from the request's hostname. There is no per-user, per-room, or cross-app scope option exposed by `useR2Files`.

```tsx theme={null}
const { upload, list } = useR2Files()             // implicit { scope: 'self' }
const { upload, list } = useR2Files({ scope: 'self' }) // equivalent
```

## Display helpers

| Helper                          | Signature                            |
| ------------------------------- | ------------------------------------ |
| `isImageFile(mimeType: string)` | Returns true for `image/*` MIMEs     |
| `formatFileSize(bytes: number)` | Returns `'1.2 MB'`, `'456 KB'`, etc. |

## Local dev limitation

<Warning>
  R2 uploads require an `APP_IDENTITY_TOKEN` minted by the deploy worker. The CLI does not provision this token locally, so `upload()` round-trips return 401 from the platform file gateway. In local dev, assert that uploads are *dispatched*; the full flow works only against [deployed apps](/concepts/deployment).
</Warning>

## See also

* [File uploads guide](/guides/file-uploads) - patterns and worked examples.
* [Custom bindings](/guides/custom-bindings) - declare your own R2 bucket with custom permissions.
