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

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="inline-block mr-2 animate-spin rounded-full border-2 border-current border-t-transparent" /> : null}
          Upload
        </button>
      </div>
      {error && <p className="text-destructive">{error.message}</p>}
      {uploadedImageUrl && (
        <img src={uploadedImageUrl} alt="Uploaded" className="w-full rounded-lg" />
      )}
    </div>
  );
}

Error Handling

Uploads return undefined on failure:

const { uploadFile, error, isLoading } = useFileUpload();

const handleUpload = async (file: File) => {
  const result = await uploadFile(file);

  if (!result) {
    console.error("Upload failed:", error?.message);
    return;
  }

  console.log("Uploaded to:", result.url);
};

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.