Skip to main content
Documentation

Files reference

`useR2Files` and file display helpers.

On this page

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.

import { useR2Files, isImageFile, formatFileSize } from 'deepspace'
import type { R2FileInfo, R2Scope } from 'deepspace'
ts

useR2Files(options?)#

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
}
ts

options is the R2Scope itself. Two scopes are valid - 'self' (the default) and 'app' - and the choice decides both where files live and who can read them. See Scopes. Every method that takes a file accepts either an R2FileInfo object from list() or a raw key string.

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.

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)
}
tsx

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

R2FileInfo#

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

Scopes#

type R2Scope = { scope?: 'self' | 'app' }
ts

Scope decides where files live and, crucially, who can read them.

ScopePrefixReads
'self' (default)apps/<app>/users/<userId>/…Require the caller's auth token. Not usable from a plain <img> or an unauthenticated request.
'app'apps/<app>/…Public. No auth header needed - the returned URL works directly as an <img src>.
// Per-user files (default) - private to the signed-in user
const { upload, downloadFile } = useR2Files()
await upload(myFile, 'photo.png')

// App-shared files - public reads, embeddable in pages
const { upload } = useR2Files({ scope: 'app' })
const r = await upload(file, `avatars/${userId}.png`)
// r.url is a plain, anon-readable URL
tsx

getUrl() attaches no auth token, so it works for 'app'-scope files but not 'self'-scope ones. For private files use readFile or downloadFile, which send the Authorization header.

Both scopes are per-app: the platform derives the bucket prefix server-side, so a key can never address another app. For finer namespacing within a scope (per-room, per-project), encode it into the key.

Display helpers#

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

Local dev limitation#

See also#