npm package: @image2url/react
Description: A powerful React hook for image uploading with built-in optimization, validation, and progress tracking.
Latest version: 0.1.0

# @image2url/react

A powerful React hook for image uploading with built-in optimization, validation, and progress tracking.

## 🚀 Features

- 🖼️ **Image Preview** - Local preview generation
- 📊 **Progress Tracking** - Real-time upload progress
- 🔍 **File Validation** - Type, size, and dimension checks
- ⚡ **Image Optimization** - Compression, WebP conversion, EXIF stripping
- 🔄 **Retry Logic** - Automatic retry with exponential backoff
- 🛡️ **Error Handling** - Comprehensive error classification
- 🎯 **TypeScript** - Full type safety
- ✂️ **Cancellable** - Upload can be aborted
- 🎛️ **Configurable** - Extensive customization options

## 📦 Installation

```bash
npm install @image2url/react
# or
yarn add @image2url/react
# or
pnpm add @image2url/react
```

## 🚀 Quick Start

```tsx
import React from 'react';
import { useImageUpload } from '@image2url/react';

function ImageUploader() {
  const {
    upload,
    isUploading,
    progress,
    previewUrl,
    url,
    error
  } = useImageUpload({
    endpoint: '/api/upload',
    onSuccess: (uploadedUrl) => console.log('Success:', uploadedUrl),
    onError: (err) => console.error('Error:', err.message)
  });

  const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      try {
        await upload(file);
      } catch (error) {
        // Error handled by onError callback
      }
    }
  };

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        onChange={handleFileChange}
        disabled={isUploading}
      />
      
      {isUploading && (
        <div className="progress">
          Uploading: {progress}%
        </div>
      )}
      
      {previewUrl && (
        <img
          src={previewUrl}
          alt="Preview"
          style={{ maxWidth: 200, maxHeight: 200 }}
        />
      )}
      
      {error && (
        <div className="error">
          Error: {error.message}
        </div>
      )}
      
      {url && (
        <div className="result">
          <h4>Uploaded!</h4>
          <img src={url} alt="Uploaded" style={{ maxWidth: 400 }} />
          <p>URL: {url}</p>
        </div>
      )}
    </div>
  );
}
```

## 📖 API Reference

### `useImageUpload(options)`

The main hook that provides image upload functionality.

#### Parameters

`options` - Optional configuration object:

```typescript
interface UseImageUploadOptions {
  // Core configuration
  endpoint?: string;              // Upload endpoint (default: '/api/image2url')
  uploadFieldName?: string;        // Form field name (default: 'file')
  headers?: Record<string, string>; // Custom HTTP headers
  
  // Preview and UX
  preview?: boolean;               // Generate local preview (default: true)
  
  // Validation limits
  limits?: {
    maxSize?: string;             // e.g., '5MB', '2GB'
    allowedTypes?: string[];      // e.g., ['image/jpeg', 'image/png']
    maxWidth?: number;            // Maximum width in pixels
    maxHeight?: number;           // Maximum height in pixels
  };
  
  // Image optimization
  optimization?: {
    compress?: boolean;           // Enable compression (default: true)
    targetSize?: string;          // Target file size
    quality?: number;             // Quality 0-1 (default: 0.8)
    stripExif?: boolean;          // Remove EXIF data (default: true)
    convertToWebP?: boolean;      // Convert to WebP (default: false)
  };
  
  // Retry configuration
  retry?: {
    attempts?: number;            // Retry attempts (default: 3)
    backoffMs?: number;           // Base backoff delay (default: 500ms)
  };
  
  // Performance
  chunkSize?: number;             // Upload chunk size in bytes (default: 5MB)
  
  // Callbacks
  onSuccess?: (url: string) => void;
  onError?: (error: Image2UrlError) => void;
}
```

#### Returns

```typescript
interface UseImageUploadReturn {
  // Actions
  upload: (file: File | Blob, options?: Partial<UseImageUploadOptions>) => Promise<UploadResult>;
  abort: () => void;
  reset: () => void;
  revokePreview: () => void;
  
  // State
  isUploading: boolean;
  progress: number;               // 0-100
  url?: string;                   // Final uploaded URL
  previewUrl?: string;            // Local preview URL
  error?: Image2UrlError;
}
```

### `upload(file, options?)`

Uploads an image file.

#### Parameters

- `file` - `File | Blob` - The image to upload
- `options` - Optional override configuration

#### Returns

`Promise<UploadResult>` - Resolves when upload is complete:

```typescript
interface UploadResult {
  url: string;                    // Uploaded image URL
  previewUrl?: string;            // Local preview URL (if generated)
  width?: number;                 // Image width
  height?: number;                // Image height
}
```

#### Example

```tsx
// Basic upload
await upload(file);

// Upload with custom options
await upload(file, {
  optimization: { quality: 0.9 },
  limits: { maxSize: '10MB' },
  onSuccess: (url) => console.log('Custom success:', url)
});
```

## 🎨 Usage Examples

### Basic Image Uploader

```tsx
import { useImageUpload } from '@image2url/react';

function BasicUploader() {
  const { upload, isUploading, progress, url } = useImageUpload({
    endpoint: '/api/upload'
  });

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        onChange={async (e) => {
          const file = e.target.files?.[0];
          if (file) await upload(file);
        }}
        disabled={isUploading}
      />
      
      {isUploading && <div>Progress: {progress}%</div>}
      {url && <img src={url} alt="Uploaded" />}
    </div>
  );
}
```

### Advanced Configuration

```tsx
import { useImageUpload } from '@image2url/react';

function AdvancedUploader() {
  const { upload, error, previewUrl } = useImageUpload({
    endpoint: '/api/upload',
    headers: {
      'Authorization': 'Bearer your-token',
      'X-Custom-Header': 'value'
    },
    
    limits: {
      maxSize: '5MB',
      allowedTypes: ['image/jpeg', 'image/png'],
      maxWidth: 1920,
      maxHeight: 1080
    },
    
    optimization: {
      compress: true,
      quality: 0.85,
      stripExif: true,
      convertToWebP: true,
      targetSize: '2MB'
    },
    
    retry: {
      attempts: 5,
      backoffMs: 1000
    },
    
    onSuccess: (url) => {
      console.log('Upload successful:', url);
      // Maybe update your state, show notification, etc.
    },
    
    onError: (error) => {
      console.error('Upload failed:', error.code, error.message);
      // Handle different error types
      switch (error.code) {
        case 'SIZE_EXCEEDED':
          alert('File too large!');
          break;
        case 'TYPE_NOT_ALLOWED':
          alert('Invalid file type!');
          break;
        default:
          alert('Upload failed: ' + error.message);
      }
    }
  });

  return (
    <div>
      {/* Your upload UI */}
      <input
        type="file"
        accept="image/*"
        onChange={async (e) => {
          const file = e.target.files?.[0];
          if (file) {
            try {
              await upload(file);
            } catch (err) {
              // Error already handled by onError callback
            }
          }
        }}
      />
      
      {error && (
        <div className="error">
          {error.message} (Code: {error.code})
        </div>
      )}
      
      {previewUrl && (
        <img src={previewUrl} alt="Preview" style={{ maxWidth: 200 }} />
      )}
    </div>
  );
}
```

### Drag and Drop Component

```tsx
import { useImageUpload } from '@image2url/react';
import { useCallback } from 'react';

function DragDropUploader() {
  const { upload, isUploading, previewUrl } = useImageUpload();
  
  const handleDrop = useCallback(async (e: React.DragEvent) => {
    e.preventDefault();
    const files = Array.from(e.dataTransfer.files);
    const imageFiles = files.filter(file => file.type.startsWith('image/'));
    
    if (imageFiles.length > 0) {
      try {
        await upload(imageFiles[0]);
      } catch (error) {
        // Handle error
      }
    }
  }, [upload]);

  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
  }, []);

  return (
    <div
      onDrop={handleDrop}
      onDragOver={handleDragOver}
      style={{
        border: '2px dashed #ccc',
        padding: '20px',
        textAlign: 'center'
      }}
    >
      {isUploading ? (
        <div>Uploading...</div>
      ) : previewUrl ? (
        <img src={previewUrl} alt="Preview" style={{ maxWidth: '100%' }} />
      ) : (
        <div>Drop an image here</div>
      )}
    </div>
  );
}
```

### Upload with Progress Bar

```tsx
import { useImageUpload } from '@image2url/react';

function ProgressBarUploader() {
  const { upload, isUploading, progress, error } = useImageUpload({
    endpoint: '/api/upload'
  });

  return (
    <div className="upload-container">
      <input
        type="file"
        accept="image/*"
        onChange={async (e) => {
          const file = e.target.files?.[0];
          if (file) await upload(file);
        }}
        disabled={isUploading}
      />
      
      {isUploading && (
        <div className="progress-container">
          <div className="progress-bar">
            <div
              className="progress-fill"
              style={{ width: `${progress}%` }}
            />
          </div>
          <span>{progress}%</span>
        </div>
      )}
      
      {error && (
        <div className="error-message">
          {error.message}
        </div>
      )}
    </div>
  );
}
```

## 🚨 Error Handling

The hook provides typed errors with specific codes:

```typescript
type Image2UrlErrorCode =
  | "TYPE_NOT_ALLOWED"     // File type not supported
  | "SIZE_EXCEEDED"       // File too large
  | "DIMENSION_EXCEEDED"  // Image dimensions too large
  | "VALIDATION_FAILED"   // General validation error
  | "NETWORK_ERROR"       // Network connectivity issue
  | "RETRY_EXHAUSTED"     // All retry attempts failed
  | "SERVER_ERROR"        // Server returned error
  | "ABORTED"             // Upload was cancelled
```

### Error Handling Example

```tsx
import { useImageUpload, Image2UrlError } from '@image2url/react';

function ErrorHandlingUploader() {
  const { upload, error } = useImageUpload({
    endpoint: '/api/upload',
    onError: (error: Image2UrlError) => {
      switch (error.code) {
        case 'TYPE_NOT_ALLOWED':
          showError('Please upload a valid image file (JPG, PNG, WebP)');
          break;
        case 'SIZE_EXCEEDED':
          showError('File is too large. Maximum size is 5MB');
          break;
        case 'DIMENSION_EXCEEDED':
          showError('Image resolution is too high. Maximum is 4K');
          break;
        case 'NETWORK_ERROR':
          showError('Network error. Please check your connection');
          break;
        case 'RETRY_EXHAUSTED':
          showError('Upload failed after multiple attempts. Please try again');
          break;
        case 'SERVER_ERROR':
          showError('Server error. Please try again later');
          break;
        case 'ABORTED':
          showError('Upload was cancelled');
          break;
        default:
          showError('Upload failed: ' + error.message);
      }
    }
  });

  // ... rest of component
}
```

## 🎛️ Advanced Features

### Custom Headers and Authentication

```tsx
const { upload } = useImageUpload({
  endpoint: '/api/upload',
  headers: {
    'Authorization': 'Bearer ' + getAuthToken(),
    'X-User-ID': userId,
    'X-App-Version': '1.0.0'
  }
});
```

### Image Optimization

```tsx
const { upload } = useImageUpload({
  optimization: {
    compress: true,
    quality: 0.8,           // 0-1, higher = better quality
    stripExif: true,         // Remove metadata
    convertToWebP: false,    // Keep original format
    targetSize: '1MB'        // Try to achieve this file size
  }
});
```

### Retry Configuration

```tsx
const { upload } = useImageUpload({
  retry: {
    attempts: 5,             // Number of retry attempts
    backoffMs: 1000          // Base delay between retries
  }
});
```

## 🏗️ TypeScript Support

The package is fully typed. You can import types for enhanced developer experience:

```typescript
import type {
  UseImageUploadOptions,
  UseImageUploadReturn,
  UploadResult,
  OptimizationConfig,
  LimitsConfig,
  RetryConfig,
  Image2UrlError,
  Image2UrlErrorCode
} from '@image2url/react';
```

## 📱 Browser Support

- Chrome 61+
- Firefox 60+
- Safari 11+
- Edge 79+

## 🔗 Related Packages

- [`@image2url/next`](../next) - Next.js API route handler
- [Image2Url Service](https://www.image2url.com) - Backend image processing service

## 📄 License

MIT © Image2Url