Editor uploads
When the user drops a file into the timeline or the sidebar, the editor calls onUpload(file). Resolve the promise with a public URL and the editor uses that for all references — playback, rendering, caching.
Signature
type UploadHandler = (file: File) => Promise<string>;S3 example
<VideoEditor
video={v}
onUpload={async (file) => {
const { url, fields } = await fetch('/api/presigned', {
method: 'POST',
body: JSON.stringify({ name: file.name, type: file.type }),
}).then((r) => r.json());
const body = new FormData();
Object.entries(fields).forEach(([k, v]) => body.append(k, v));
body.append('file', file);
await fetch(url, { method: 'POST', body });
return `${url}/${fields.key}`;
}}
/>Cloudflare R2 / Supabase
<VideoEditor
onUpload={async (file) => {
const form = new FormData();
form.append('file', file);
const res = await fetch('/api/upload', { method: 'POST', body: form });
const { url } = await res.json();
return url;
}}
/>Fallback: object URLs
If you don't pass onUpload, the editor wraps dropped files with URL.createObjectURL() and revokes them on unmount. Great for demos — a dead end for anything persisted.
CORS. Whatever URL you return must be reachable with CORS enabled so the editor's renderer can decode it. S3 and R2 both need an explicit GET policy allowing your app origin.