How it works
1
Generate an upload URL
A Convex mutation calls
ctx.storage.generateUploadUrl() and returns a one-time URL.2
Client uploads directly
await fetch(uploadUrl, { method: "POST", body: file }) — bytes go straight to Convex storage, not through your function.3
Save the storage ID
Mutation stores
storageId on a row in your schema (e.g., users.avatarStorageId).4
Serve via URL
Read with
ctx.storage.getUrl(storageId) — returns a CDN URL good for serving.Example
convex/files.ts
Limits
For files larger than 100 MB, use a multi-part upload pattern or store in S3 / R2 via the AWS S3 integration.
Image transformations
Append query params to the URL for on-the-fly transformations:Tips
Related
File uploads recipe
Complete walkthrough.
Cloudinary
For advanced image transformations.
Convex actions
For uploads that need to call third-party APIs.
