DeepSpace apps get a per-app R2 bucket out of the box. TheDocumentation Index
Fetch the complete documentation index at: https://docs.deep.space/llms.txt
Use this file to discover all available pages before exploring further.
useR2Files hook covers uploads, listings, deletions, and authenticated downloads - all proxied through the platform-worker so the app never holds R2 credentials directly.
This guide shows the common end-to-end flows. For the full method signatures and types, see the files reference.
How files are wired
You don’t add an R2 binding yourself. The starter worker already proxies/api/files/* to the platform-worker, which holds a shared bucket and namespaces keys per app (via the APP_NAME the worker forwards on each request). Every write is gated by the caller’s signed JWT.
The client side is a single hook:
Upload from a file input
The most common case - an<input type="file"> or drag-drop event. Pass the resulting File to upload:
result.success before reading result.key - the upload may fail (network, auth). isUploading is true while a request is in flight.
Upload generated data (canvas, cropped image)
When you have data as a Base64 string - for example, from<canvas>.toDataURL() - use uploadBase64. The display name is required so the file has an originalName for later downloads:
List and render a user’s files
list() is an async function - call it and store the result in state rather than reading a reactive array:
list() after mutations - there’s no reactive cache. formatFileSize and isImageFile are display helpers exported from deepspace.
Authenticated downloads
getUrl(fileOrKey) returns a plain URL with no auth token attached. It works for unauthenticated reads on deployed sites (the platform-worker resolves the app from APP_NAME and serves reads without a JWT), which is what you want for <img src>. For everything else, use downloadFile or readFile:
R2FileInfo from list() or a raw key string.
Storing metadata (MIME type, captions, tags)
R2FileInfo carries key, size, uploaded, url, originalName, and uploadedBy - and nothing else. There’s no mimeType field. For richer metadata, store a sidecar record in a collection:
RecordProvider higher in the tree that has registered the attachments collection - useMutations throws otherwise.
Scoping and permissions
useR2Files is always scoped to the current app - the platform derives the bucket prefix from APP_NAME. There’s no per-user or cross-app scope exposed by the hook. If you need finer namespacing (per-room, per-project), encode it into the name argument at upload time or store it on a sidecar record.
Writes require a signed user JWT. Anyone who knows a file’s key and hits your app’s /api/files/<key> can read it; if a file should be private, gate access in your UI and use readFile/downloadFile rather than rendering its URL. There is no recycle bin - deleteFile is immediate and irreversible.
Local development
For tests written before the first deploy, assert that uploads are dispatched (the function is called) rather than asserting on the round-trip. Or runnpx deepspace dev --prod to point local UI at production workers.
Next steps
- Files reference - full method signatures, return types, and the
R2FileInfoshape. - Custom bindings - declare a wholly separate R2 bucket with custom permissions.
- Data model - pair files with sidecar records for queryable metadata.