Examples & Demos

Experience pushduck with interactive demos and real-world examples. All demos use live Cloudflare R2 integration.

Live Demos: These are fully functional demos using real Cloudflare R2 storage. Files are uploaded to a demo bucket and may be automatically cleaned up. Don't upload sensitive information.

Having Issues? If uploads aren't working (especially with next dev --turbo), check our Troubleshooting Guide for common solutions including the known Turbo mode compatibility issue.

Interactive Upload Demo

The full-featured demo showcasing all capabilities:

🚀 Full-Featured Demo

Complete upload experience with type-safe client, progress tracking, and error handling

ℹ️

Live Demo: Files are uploaded to Cloudflare R2. Don't upload sensitive information.

✅ Type-Safe⚡ Property-Based📊 Overall Progress☁️ Cloudflare R2
📤

Drop files above or click to browse

💻 Code powering this demo:

// Enhanced type-safe client
const imageUpload = upload.imageUpload();

// Upload with full type safety
await imageUpload.uploadFiles(files);

// Access state with TypeScript inference
const { 
  files,        // Individual file progress
  isUploading,  // Upload status
  errors,       // Any errors
  progress,     // Overall progress (0-100)
  uploadSpeed,  // Overall bytes/sec
  eta          // Overall time remaining
} = imageUpload;

ETA & Speed Tracking: Upload speed (MB/s) and estimated time remaining (ETA) appear below the progress bar during active uploads. Try uploading larger files (1MB+) to see these metrics in action! ETA becomes more accurate after the first few seconds of upload.

Image-Only Upload

Focused demo for image uploads with preview capabilities:

🖼️ Image Upload Demo

Optimized for images with instant previews and validation

📤

Drop files above or click to browse

Document Upload

Streamlined demo for document uploads:

📄 Document Upload Demo

Professional document handling with type validation

📤

Drop files above or click to browse

Key Features Demonstrated

Type-Safe Client

// Property-based access with full TypeScript inference
const imageUpload = upload.imageUpload();
const fileUpload = upload.fileUpload();

// No string literals, no typos, full autocomplete
await imageUpload.uploadFiles(selectedFiles);

Real-Time Progress

  • Individual file progress tracking with percentage completion
  • Upload speed monitoring (MB/s) with live updates
  • ETA calculations showing estimated time remaining
  • Pause/resume functionality (coming soon)
  • Comprehensive error handling with retry mechanisms

🔒 Built-in Validation

  • File type validation (MIME types)
  • File size limits with user-friendly errors
  • Custom validation middleware
  • Malicious file detection

🌐 Provider Agnostic

  • Same code works with any S3-compatible provider
  • Switch between Cloudflare R2, AWS S3, DigitalOcean Spaces
  • Zero vendor lock-in

Code Examples

"use client";

import { upload } from "@/lib/upload-client";

export function SimpleUpload() {
  const { uploadFiles, files, isUploading } = upload.imageUpload();
  
  return (
    <div>
      <input
        type="file"
        multiple
        accept="image/*"
        onChange={(e) => uploadFiles(Array.from(e.target.files || []))}
        disabled={isUploading}
      />
      
      {files.map(file => (
        <div key={file.id}>
          <span>{file.name}</span>
          <span>{file.status}</span>
          {file.url && <a href={file.url}>View</a>}
        </div>
      ))}
    </div>
  );
}
// app/api/upload/route.ts
import { createUploadConfig } from "pushduck/server";

const { s3,  } = createUploadConfig()
  .provider("cloudflareR2",{
    accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
    bucket: process.env.R2_BUCKET!,
  })
  .defaults({
    maxFileSize: "10MB",
    acl: "public-read",
  })
  .build();

const uploadRouter = s3.createRouter({
  imageUpload: s3
    .image()
    .max("5MB")
    .formats(["jpeg", "png", "webp"])
    .middleware(async ({ file, metadata }) => {
      // Custom authentication and metadata
      const session = await getServerSession();
      if (!session) throw new Error("Unauthorized");
      
      return {
        ...metadata,
        userId: session.user.id,
        uploadedAt: new Date().toISOString(),
      };
    })
    .onUploadComplete(async ({ file, url, metadata }) => {
      // Post-upload processing
      console.log(`Upload complete: ${url}`);
      await saveToDatabase({ url, metadata });
    }),
});

export const { GET, POST } = uploadRouter.handlers;
export type AppRouter = typeof uploadRouter;
"use client";

import { upload } from "@/lib/upload-client";

export function RobustUpload() {
  const { uploadFiles, files, errors, reset } = upload.imageUpload();
  
  const handleUpload = async (fileList: FileList) => {
    try {
      await uploadFiles(Array.from(fileList));
    } catch (error) {
      console.error("Upload failed:", error);
      // Error is automatically added to the errors array
    }
  };
  
  return (
    <div>
      <input
        type="file"
        onChange={(e) => e.target.files && handleUpload(e.target.files)}
      />
      
      {/* Display errors */}
      {errors.length > 0 && (
        <div className="error-container">
          <h4>Upload Errors:</h4>
          {errors.map((error, index) => (
            <p key={index} className="error">{error}</p>
          ))}
          <button onClick={reset}>Clear Errors</button>
        </div>
      )}
      
      {/* Display files with status */}
      {files.map(file => (
        <div key={file.id} className={`file-item ${file.status}`}>
          <span>{file.name}</span>
          <span>{file.status}</span>
          {file.status === "uploading" && (
            <progress value={file.progress} max={100} />
          )}
          {file.status === "error" && (
            <span className="error">{file.error}</span>
          )}
          {file.status === "success" && file.url && (
            <a href={file.url} target="_blank">View File</a>
          )}
        </div>
      ))}
    </div>
  );
}

Real-World Use Cases

Profile Picture Upload

Single image upload with instant preview and crop functionality.

Document Management

Multi-file document upload with categorization and metadata.

Batch image upload with automatic optimization and thumbnail generation.

File Sharing

Secure file upload with expiration dates and access controls.

Next Steps