useUploadRoute
React hook for uploading files with reactive state management
useUploadRoute
A React hook that provides reactive state management for file uploads with progress tracking and error handling.
This hook follows familiar React patterns and is perfect for teams that prefer
the traditional hook-based approach. Both this and createUploadClient
are equally valid ways to handle uploads.
When to Use This Hook
Use useUploadRoute
when:
- 🪝 Prefer React hooks - familiar pattern for React developers
- 🧩 Granular control needed over individual upload state
- 🔄 Component-level state management preferred
- 👥 Team preference for hook-based patterns
Alternative Approach
You can also use the structured client approach:
// Hook-based approach
import { useUploadRoute } from 'pushduck/client'
const { uploadFiles, files } = useUploadRoute<AppRouter>('imageUpload')
// Structured client approach
import { createUploadClient } from 'pushduck/client'
const upload = createUploadClient<AppRouter>({
endpoint: '/api/upload'
})
const { uploadFiles, files } = upload.imageUpload()
Both approaches provide the same functionality and type safety - choose what feels more natural for your team.
Basic Usage
import { useUploadRoute } from "pushduck/client";
import { formatETA, formatUploadSpeed } from "pushduck";
import type { AppRouter } from "@/lib/upload";
export function ImageUploader() {
// With type parameter (recommended for better type safety)
const { uploadFiles, files, isUploading, error, reset, progress, uploadSpeed, eta } =
useUploadRoute<AppRouter>("imageUpload");
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFiles = Array.from(e.target.files || []);
uploadFiles(selectedFiles);
};
return (
<div>
<input
type="file"
multiple
accept="image/*"
onChange={handleFileSelect}
/>
{/* Overall Progress Tracking */}
{isUploading && files.length > 1 && progress !== undefined && (
<div className="overall-progress">
<h3>Overall Progress: {Math.round(progress)}%</h3>
<progress value={progress} max={100} />
<div>
<span>Speed: {uploadSpeed ? formatUploadSpeed(uploadSpeed) : '0 B/s'}</span>
<span>ETA: {eta ? formatETA(eta) : '--'}</span>
</div>
</div>
)}
{/* Individual File Progress */}
{files.map((file) => (
<div key={file.id}>
<span>{file.name}</span>
<progress value={file.progress} max={100} />
{file.status === "success" && <a href={file.url}>View</a>}
</div>
))}
{error && <div className="error">{error.message}</div>}
<button onClick={reset}>Reset</button>
</div>
);
}
Overall Progress Tracking
The hook provides real-time overall progress metrics when uploading multiple files:
Overall progress tracking is especially useful for batch uploads and provides a better user experience when uploading multiple files simultaneously.
const { progress, uploadSpeed, eta } = useUploadRoute("imageUpload");
// Progress: 0-100 percentage across all files
console.log(`Overall progress: ${progress}%`);
// Upload speed: Combined transfer rate in bytes/second
console.log(`Transfer rate: ${formatUploadSpeed(uploadSpeed)}`);
// ETA: Time remaining in seconds
console.log(`Time remaining: ${formatETA(eta)}`);
Progress Calculation
- progress: Weighted by file sizes, not just file count
- uploadSpeed: Sum of all active file upload speeds
- eta: Calculated based on remaining bytes and current speed
- Values are
undefined
when no uploads are active
Hook Signature
// With type parameter (recommended)
function useUploadRoute<TRouter>(
route: keyof TRouter,
options?: UseUploadOptions
): UseUploadReturn;
// Without type parameter (also works)
function useUploadRoute(
route: string,
options?: UseUploadOptions
): UseUploadReturn;
Parameters
Prop | Type | Default |
---|---|---|
options? | UseUploadOptions | - |
route | keyof TRouter | string | - |
Type Parameter Benefits
// ✅ With type parameter - better type safety
const { uploadFiles } = useUploadRoute<AppRouter>("imageUpload");
// - Route names are validated at compile time
// - IntelliSense shows available routes
// - Typos caught during development
// ✅ Without type parameter - still works
const { uploadFiles } = useUploadRoute("imageUpload");
// - Works with any string
// - Less type safety but more flexible
// - Good for dynamic route names
Options
Prop | Type | Default |
---|---|---|
autoUpload? | boolean | true |
disabled? | boolean | false |
onProgress? | (progress: UploadProgress) => void | - |
onError? | (error: UploadError) => void | - |
onSuccess? | (results: UploadResult[]) => void | - |
Return Value
Prop | Type | Default |
---|---|---|
eta? | number | undefined | - |
uploadSpeed? | number | undefined | - |
progress? | number | undefined | - |
reset? | () => void | - |
error? | UploadError | null | - |
uploadedFiles? | UploadResult[] | - |
isUploading? | boolean | - |
files? | UploadFile[] | - |
uploadFiles? | (files: File[]) => Promise<UploadResult[]> | - |
Advanced Examples
const { uploadFiles, files } = useUploadRoute<AppRouter>('documentUpload', {
onSuccess: (results) => {
toast.success(`Uploaded ${results.length} files`)
updateDocuments(results)
},
onError: (error) => {
toast.error(`Upload failed: ${error.message}`)
},
onProgress: (progress) => {
setGlobalProgress(progress.percentage)
}
})
const images = useUploadRoute<AppRouter>('imageUpload')
const documents = useUploadRoute<AppRouter>('documentUpload')
return (
<div>
<FileUploadSection {...images} accept="image/*" />
<FileUploadSection {...documents} accept=".pdf,.doc" />
</div>
)
const { uploadFiles, uploadedFiles } = useUploadRoute<AppRouter>('attachments', {
onSuccess: (results) => {
setValue('attachments', results.map(r => r.url))
}
})
const onSubmit = (data) => {
// Form data includes uploaded file URLs
console.log(data.attachments)
}
Flexible API: Use this hook when you prefer React's familiar hook patterns or need more granular control over upload state.