File Upload Reference
Use useFileUpload to upload user-selected files to blob storage and receive a public URL that can be displayed, persisted, passed to AI hooks, or downloaded later.
File URLs are unguessable, but anyone with the URL can access the file without signing in. App visibility does not restrict access to a file URL. Uploads and AI attachments remain in the user's account storage until deleted, including uploads made in preview. See file privacy.
useFileUpload
Returns
uploadFile(function): Upload aFileand resolve to the result shape below orundefined.isLoading(boolean): Whether an upload is in progress.error(Error | null): Current upload error.file(object | null): Last uploaded file result.
Result Shape
name(string): Original file name.contentType(string): MIME type.url(string): Public URL to the uploaded file.
Supported File Types
Common supported MIME types include:
- Images:
image/png,image/jpeg,image/webp,image/heic, andimage/heif. - Audio:
audio/mpeg,audio/mp3,audio/wav,audio/aiff,audio/x-aiff,audio/aac,audio/ogg, andaudio/flac. - Video:
video/mp4,video/mpeg,video/mov,video/avi,video/x-flv,video/mpg,video/webm,video/wmv, andvideo/3gpp. - Documents:
application/pdf.
Example
Import the hook from the file upload module:
import { useFileUpload } from '@/hooks/use-file-upload'
import { usePersistentItem } from '@/hooks/use-persistent-item'
export default function App() {
const [uploadedImageUrl, setUploadedImageUrl] = usePersistentItem<
string | null
>('uploadedImageUrl', null)
const [selectedFile, setSelectedFile] = React.useState<File | null>(null)
const { uploadFile, isLoading, error } = useFileUpload()
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSelectedFile(event.target.files?.[0] ?? null)
}
const handleUpload = async () => {
if (!selectedFile) return
const result = await uploadFile(selectedFile)
if (result) {
setUploadedImageUrl(result.url)
setSelectedFile(null)
}
}
return (
<div>
<div className="flex gap-2">
<input
type="file"
accept="image/png, image/jpeg, image/webp"
onChange={handleFileChange}
/>
<button onClick={handleUpload} disabled={!selectedFile || isLoading}>
{isLoading ? (
<span className="mr-2 inline-block animate-spin rounded-full border-2 border-current border-t-transparent" />
) : null}
Upload
</button>
</div>
{error ? <p className="text-destructive">{error.message}</p> : null}
{uploadedImageUrl ? (
<img
src={uploadedImageUrl}
alt="Uploaded"
className="w-full rounded-lg"
/>
) : null}
</div>
)
}
Error Handling
Uploads return undefined on failure:
const { uploadFile, error } = useFileUpload()
const handleUpload = async (file: File) => {
const result = await uploadFile(file)
if (!result) {
return
}
console.log('Uploaded to:', result.url)
}
Render error from the hook in component JSX. React state updates on the next render, so the value in the same event-handler closure may still be from the previous render immediately after await uploadFile(...).
Best Practices
- Upload one file at a time per hook instance. Use multiple hook instances for concurrent uploads.
- Persist
result.urlwithusePersistentItemwhen the file should survive reloads. - Stream files directly to storage instead of reading large files into memory.
- Use the returned
contentTypewhen displaying or categorizing uploaded files. - Pass uploaded URLs to AI hooks as
{ url }attachments when you need AI processing. - Keep local
Filestate separate from persisted URLs so users can clear or retry selections cleanly.