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.

useFileUpload

Returns

  • uploadFile (function): Upload a File and resolve to the result shape below or undefined.
  • 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, and image/heif.
  • Audio: audio/mpeg, audio/mp3, audio/wav, audio/aiff, audio/x-aiff, audio/aac, audio/ogg, and audio/flac.
  • Video: video/mp4, video/mpeg, video/mov, video/avi, video/x-flv, video/mpg, video/webm, video/wmv, and video/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.url with usePersistentItem when the file should survive reloads.
  • Stream files directly to storage instead of reading large files into memory.
  • Use the returned contentType when displaying or categorizing uploaded files.
  • Pass uploaded URLs to AI hooks as { url } attachments when you need AI processing.
  • Keep local File state separate from persisted URLs so users can clear or retry selections cleanly.