API Reference/Hooks

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

PropTypeDefault
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

PropTypeDefault
autoUpload?
boolean
true
disabled?
boolean
false
onProgress?
(progress: UploadProgress) => void
-
onError?
(error: UploadError) => void
-
onSuccess?
(results: UploadResult[]) => void
-

Return Value

PropTypeDefault
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.