The standard pattern
1
Client requests an upload URL
Mutation:
generateUploadUrl() returns a one-time URL.2
Client uploads bytes directly
fetch(url, { method: "POST", body: file }).3
Client gets back a storage ID
Records the ID in the relevant row via a separate mutation.
4
Server serves the file
Via the storage URL when needed.
Validation
Validate on the client (UX) AND on the server (security):Previews
For images, generate a thumbnail at upload:Multiple files
For multi-file uploads (drag-and-drop a folder), upload in parallel with concurrency control:Limits
- Convex max per file: 100 MB by default.
- Larger files: use AWS S3 integration for multipart uploads.
Related
File uploads recipe
Worked walkthrough.
Convex file storage
Reference.
Cloudinary
Image transformations.
