File Upload Component

The FileUpload component provides a beautiful drag-and-drop interface for uploading files with built-in validation, progress tracking, and preview.

Basic Usage

import { FileUpload } from '@dropper/react'

function App() {
  return (
    <FileUpload
      onSuccess={(file) => console.log('Uploaded:', file.url)}
      onError={(error) => console.error('Upload failed:', error)}
    />
  )
}

Props

interface FileUploadProps {
  folderId?: string
  accept?: string
  maxSize?: number
  maxFiles?: number
  multiple?: boolean
  disabled?: boolean
  showPreview?: boolean
  showProgress?: boolean
  className?: string
  metadata?: Record<string, unknown>
  onSuccess?: (file: FileResponseDto) => void
  onError?: (error: Error, file: File) => void
  onProgress?: (progress: number, file: File) => void
  onValidationError?: (errors: ValidationError[]) => void
  onFilesSelected?: (files: File[]) => void
}

folderId

Upload files to a specific folder:

<FileUpload
  folderId="folder_abc123"
  onSuccess={(file) => console.log('Uploaded to folder')}
/>

accept

Restrict file types using MIME types or extensions:

{/* Images only */}
<FileUpload accept="image/*" />

{/* Specific types */}
<FileUpload accept="image/jpeg,image/png,image/gif" />

{/* Multiple formats */}
<FileUpload accept="image/*,.pdf,.doc,.docx" />

maxSize

Set maximum file size in bytes:

{/* 5MB limit */}
<FileUpload maxSize={5 * 1024 * 1024} />

{/* 10MB limit (default) */}
<FileUpload maxSize={10 * 1024 * 1024} />

maxFiles

Limit the number of files:

{/* Single file */}
<FileUpload maxFiles={1} multiple={false} />

{/* Up to 10 files (default) */}
<FileUpload maxFiles={10} />

{/* Unlimited */}
<FileUpload maxFiles={Infinity} />

multiple

Enable multiple file selection:

{/* Multiple files (default) */}
<FileUpload multiple />

{/* Single file only */}
<FileUpload multiple={false} maxFiles={1} />

disabled

Disable the upload component:

const [uploading, setUploading] = useState(false)

<FileUpload
  disabled={uploading}
  onSuccess={() => setUploading(false)}
/>

showPreview

Show file previews after selection:

{/* Show previews (default) */}
<FileUpload showPreview />

{/* Hide previews */}
<FileUpload showPreview={false} />

showProgress

Display upload progress:

{/* Show progress (default) */}
<FileUpload showProgress />

{/* Hide progress */}
<FileUpload showProgress={false} />

metadata

Attach custom metadata to uploaded files:

<FileUpload
  metadata={{
    userId: '123',
    category: 'profile-pictures',
    tags: ['avatar', 'user'],
  }}
/>

Event Handlers

onSuccess

Called when a file uploads successfully:

<FileUpload
  onSuccess={(file) => {
    console.log('File uploaded!')
    console.log('URL:', file.url)
    console.log('ID:', file.id)
    console.log('Size:', file.size)
  }}
/>

onError

Called when upload fails:

<FileUpload
  onError={(error, file) => {
    console.error(`Failed to upload ${file.name}:`, error.message)
    showNotification(`Upload failed: ${error.message}`)
  }}
/>

onProgress

Track upload progress:

<FileUpload
  onProgress={(progress, file) => {
    console.log(`${file.name}: ${progress}%`)
    updateProgressBar(file.name, progress)
  }}
/>

onValidationError

Handle validation errors:

<FileUpload
  onValidationError={(errors) => {
    errors.forEach((error) => {
      console.error(`${error.file.name}: ${error.message}`)
    })
  }}
/>

onFilesSelected

Called when files are selected (before upload):

<FileUpload
  onFilesSelected={(files) => {
    console.log(`Selected ${files.length} files`)
    // Files won't upload automatically when this is provided
    // You control when to upload
  }}
/>

Complete Examples

Image Upload

import { FileUpload } from '@dropper/react'
import { useState } from 'react'

function ImageUpload() {
  const [uploadedImages, setUploadedImages] = useState([])
  
  return (
    <div>
      <FileUpload
        accept="image/*"
        maxSize={5 * 1024 * 1024} // 5MB
        maxFiles={10}
        multiple
        showPreview
        onSuccess={(file) => {
          setUploadedImages((prev) => [...prev, file])
          console.log('Image uploaded:', file.url)
        }}
        onError={(error, file) => {
          console.error(`Failed to upload ${file.name}:`, error)
        }}
      />
      
      <div className="grid grid-cols-3 gap-4 mt-4">
        {uploadedImages.map((image) => (
          <img
            key={image.id}
            src={image.url}
            alt={image.originalFilename}
            className="w-full h-32 object-cover rounded"
          />
        ))}
      </div>
    </div>
  )
}

Document Upload

import { FileUpload } from '@dropper/react'

function DocumentUpload() {
  return (
    <FileUpload
      accept=".pdf,.doc,.docx,.txt"
      maxSize={10 * 1024 * 1024} // 10MB
      maxFiles={5}
      folderId="folder_documents"
      metadata={{
        category: 'documents',
        uploadedBy: 'user_123',
      }}
      onSuccess={(file) => {
        console.log('Document uploaded:', file.originalFilename)
      }}
      onValidationError={(errors) => {
        errors.forEach((error) => {
          alert(`${error.file.name}: ${error.message}`)
        })
      }}
    />
  )
}

Profile Picture Upload

import { FileUpload } from '@dropper/react'
import { useState } from 'react'

function ProfilePictureUpload() {
  const [profilePicture, setProfilePicture] = useState(null)
  const [uploading, setUploading] = useState(false)
  
  return (
    <div>
      {profilePicture && (
        <img
          src={profilePicture.url}
          alt="Profile"
          className="w-32 h-32 rounded-full object-cover mb-4"
        />
      )}
      
      <FileUpload
        accept="image/jpeg,image/png"
        maxSize={2 * 1024 * 1024} // 2MB
        maxFiles={1}
        multiple={false}
        disabled={uploading}
        metadata={{ type: 'profile-picture' }}
        onSuccess={(file) => {
          setProfilePicture(file)
          setUploading(false)
        }}
        onError={(error) => {
          console.error('Upload failed:', error)
          setUploading(false)
        }}
        onProgress={(progress) => {
          setUploading(true)
          console.log(`Upload progress: ${progress}%`)
        }}
      />
    </div>
  )
}

Upload with Confirmation

import { FileUpload } from '@dropper/react'
import { useState } from 'react'

function UploadWithConfirmation() {
  const [selectedFiles, setSelectedFiles] = useState([])
  const [uploading, setUploading] = useState(false)
  
  const handleUpload = async () => {
    setUploading(true)
    // Handle upload manually
    // This example shows the pattern, actual implementation
    // would use the client directly
  }
  
  return (
    <div>
      <FileUpload
        onFilesSelected={(files) => {
          setSelectedFiles(files)
          // Files won't upload automatically
        }}
      />
      
      {selectedFiles.length > 0 && (
        <div className="mt-4">
          <p>Selected {selectedFiles.length} files</p>
          <button
            onClick={handleUpload}
            disabled={uploading}
            className="btn-primary"
          >
            {uploading ? 'Uploading...' : 'Confirm Upload'}
          </button>
        </div>
      )}
    </div>
  )
}

Upload to Folder

import { FileUpload } from '@dropper/react'
import { useState } from 'react'

function FolderUpload() {
  const [currentFolder, setCurrentFolder] = useState('folder_123')
  
  return (
    <div>
      <select
        value={currentFolder}
        onChange={(e) => setCurrentFolder(e.target.value)}
      >
        <option value="folder_123">Documents</option>
        <option value="folder_456">Images</option>
        <option value="folder_789">Videos</option>
      </select>
      
      <FileUpload
        folderId={currentFolder}
        onSuccess={(file) => {
          console.log(`Uploaded to ${currentFolder}:`, file.url)
        }}
      />
    </div>
  )
}

Styling

Custom Class Name

<FileUpload
  className="my-custom-upload"
  onSuccess={(file) => console.log('Uploaded:', file)}
/>

Themed Upload

The component automatically uses the theme from DropperProvider:

<DropperProvider
  publishableKey="pk_dropper_test_xxx"
  theme={{ primary: '#10b981' }}
>
  <FileUpload /> {/* Uses green theme */}
</DropperProvider>

Validation

The component includes built-in validation:

File Type Validation

<FileUpload
  accept="image/*"
  onValidationError={(errors) => {
    // Error: "File type not allowed"
  }}
/>

File Size Validation

<FileUpload
  maxSize={5 * 1024 * 1024}
  onValidationError={(errors) => {
    // Error: "File size exceeds 5 MB"
  }}
/>

File Count Validation

<FileUpload
  maxFiles={10}
  onValidationError={(errors) => {
    // Error: "Maximum 10 files allowed"
  }}
/>

Features

Drag and Drop

  • Drag files over the component to upload
  • Visual feedback when dragging
  • Drop files to start upload

File Previews

  • Image previews with thumbnails
  • File icons for documents
  • File size and name display
  • Remove files before upload

Progress Tracking

  • Individual file progress bars
  • Overall upload progress
  • Upload status indicators
  • Success/error states

Validation

  • File type validation
  • File size validation
  • File count validation
  • Custom validation rules

Accessibility

The component is fully accessible:

  • Keyboard navigation support
  • Screen reader friendly
  • ARIA labels and roles
  • Focus management

Best Practices

1. Set Appropriate Limits

{/* Images: 5-10MB */}
<FileUpload accept="image/*" maxSize={5 * 1024 * 1024} />

{/* Videos: 50-100MB */}
<FileUpload accept="video/*" maxSize={100 * 1024 * 1024} />

{/* Documents: 10-20MB */}
<FileUpload accept=".pdf,.doc" maxSize={10 * 1024 * 1024} />

2. Provide Feedback

<FileUpload
  onSuccess={(file) => {
    showNotification('File uploaded successfully!')
  }}
  onError={(error) => {
    showNotification(`Upload failed: ${error.message}`, 'error')
  }}
/>

3. Handle Errors Gracefully

<FileUpload
  onError={(error, file) => {
    if (error.message.includes('size')) {
      alert('File is too large')
    } else if (error.message.includes('type')) {
      alert('File type not supported')
    } else {
      alert('Upload failed. Please try again.')
    }
  }}
/>

4. Use Metadata

<FileUpload
  metadata={{
    userId: currentUser.id,
    timestamp: new Date().toISOString(),
    source: 'web-app',
  }}
/>

TypeScript Support

The component is fully typed:

import type { FileUploadProps, FileResponseDto } from '@dropper/react'

const props: FileUploadProps = {
  accept: 'image/*',
  maxSize: 5 * 1024 * 1024,
  onSuccess: (file: FileResponseDto) => {
    console.log(file.url)
  },
}

<FileUpload {...props} />

Next Steps