Hooks
useFileUpload
A React hook that manages file uploads with GCS presigned URLs via Uppy. Handles progress tracking, retry logic, file state management, and provides dropzone integration helpers.
Installation
bunx @happlyui/cli@latest add use-file-upload
Dependencies
npm packages
@uppy/core@uppy/xhr-upload
Usage
import { useFileUpload } from "@/hooks/use-file-upload"
const upload = useFileUpload({
endpoint: '/api/assets/pre-signed',
providerId: workspace.id,
assetType: 'attachment',
headers: () => ({ Authorization: `Bearer ${token}` }),
})
Examples
Document Upload with Dropzone
Combine useFileUpload with FileUpload.Dropzone and FileCard.Item to create a full upload experience with minimal boilerplate.
import { useFileUpload } from "@/hooks/use-file-upload"
import * as FileUpload from "@/components/ui/file-upload"
import * as FileCard from "@/components/ui/file-card"
function DocumentUploader() {
const upload = useFileUpload({
endpoint: '/api/assets/pre-signed',
providerId: workspace.id,
assetType: 'attachment',
headers: () => ({ Authorization: `Bearer ${token}` }),
maxFiles: 5,
allowedFileTypes: ['application/pdf'],
onUploadSuccess: (file) => saveToForm(file.url),
})
return (
<div className="space-y-3">
<FileUpload.Dropzone
type="document"
{...upload.getRootProps()}
inputProps={upload.getInputProps()}
/>
{upload.files.map((file) => (
<FileCard.Item
key={file.id}
file={file}
variant="compact"
onRemove={() => upload.removeFile(file.id)}
onRetry={() => upload.retryFile(file.id)}
onClose={() => upload.removeFile(file.id)}
/>
))}
</div>
)
}
Logo Upload
Pair useFileUpload with LogoUpload.Item for a complete logo upload experience with avatar preview.
import { useFileUpload } from "@/hooks/use-file-upload"
import * as LogoUpload from "@/components/ui/logo-upload"
function BusinessLogoUploader() {
const upload = useFileUpload({
endpoint: '/api/assets/pre-signed',
providerId: workspace.id,
assetType: 'logo',
acl: 'public-read',
maxFiles: 1,
allowedFileTypes: ['image/jpeg', 'image/png'],
maxFileSize: 3 * 1024 * 1024,
onUploadSuccess: (file) => updateLogo(file.url),
})
return (
<>
<LogoUpload.Item
file={upload.files[0]}
label="Business logo"
description={
<>
<p>Supports JPEG or PNG files (max 3MB).</p>
<p>Use a horizontal image (16:9). Best size: 1200 × 675 px.</p>
</>
}
onButtonClick={upload.openFilePicker}
/>
<input {...upload.getInputProps()} />
</>
)
}
API Reference
useFileUpload Options
Configuration options passed to the hook.
| Prop | Type | Default | Description |
|---|---|---|---|
endpoint | string | - | The API endpoint for obtaining presigned upload URLs (e.g. '/api/assets/pre-signed'). |
baseUrl | string | '' | Base URL prepended to the endpoint. |
headers | () => Record<string, string> | - | Function returning headers for the presigned URL request (e.g. auth tokens). |
providerId | string | - | The provider/workspace ID sent to the presigned URL endpoint. |
assetType | string | - | The asset type sent to the presigned URL endpoint (matches backend AssetType enum). |
acl | 'private' | 'public-read' | 'private' | Access control level for the uploaded file. |
maxFiles | number | 1 | Maximum number of files allowed. |
maxFileSize | number | 5242880 | Maximum file size in bytes (default 5MB). |
allowedFileTypes | string[] | - | Allowed MIME types or file extensions. |
onUploadSuccess | (file: UploadFile) => void | - | Called when a file upload completes successfully. |
onUploadError | (file: UploadFile, error: Error) => void | - | Called when a file upload fails. |
onFileRemove | (file: UploadFile) => void | - | Called when a file is removed from the list. |
useFileUpload Return Value
The object returned by the hook.
| Prop | Type | Default | Description |
|---|---|---|---|
files | UploadFile[] | - | Array of all files with their current upload state. |
isUploading | boolean | - | True if any file is currently uploading. |
isDraggingOver | boolean | - | True when a file is being dragged over the dropzone (via getRootProps). |
addFiles | (files: File[]) => void | - | Add native File objects to the upload queue. |
removeFile | (id: string) => void | - | Remove a file by ID. Cancels upload if in progress. |
retryFile | (id: string) => void | - | Retry a failed upload by re-adding the original file. |
clearFiles | () => void | - | Remove all files and revoke preview URLs. |
openFilePicker | () => void | - | Programmatically open the native file picker dialog. |
getInputProps | () => InputHTMLAttributes | - | Returns props to spread on a hidden <input type="file">. |
getRootProps | () => { onDrop, onDragOver, onDragEnter, onDragLeave } | - | Returns drag event handlers to spread on a dropzone container. Tracks isDraggingOver state internally. |
UploadFile
The file state object tracked by the hook.
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | - | Unique file identifier (from Uppy). |
name | string | - | Original file name. |
size | number | - | File size in bytes. |
type | string | - | MIME type. |
progress | number | - | Upload progress from 0 to 100. |
status | 'pending' | 'uploading' | 'completed' | 'failed' | - | Current upload status. |
url | string | - | Public URL after successful upload. |
key | string | - | GCS storage path/key. |
preview | string | - | Object URL for image file previews (revoked on removal). |
error | string | - | Error message if upload failed. |