@image2url/react
v0.1.0
Published
A powerful React hook for image uploading with built-in optimization, validation, and progress tracking.
Readme
@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
npm install @image2url/react
# or
yarn add @image2url/react
# or
pnpm add @image2url/react🚀 Quick Start
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:
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
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 uploadoptions- Optional override configuration
Returns
Promise<UploadResult> - Resolves when upload is complete:
interface UploadResult {
url: string; // Uploaded image URL
previewUrl?: string; // Local preview URL (if generated)
width?: number; // Image width
height?: number; // Image height
}Example
// 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
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
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
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
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:
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 cancelledError Handling Example
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
const { upload } = useImageUpload({
endpoint: '/api/upload',
headers: {
'Authorization': 'Bearer ' + getAuthToken(),
'X-User-ID': userId,
'X-App-Version': '1.0.0'
}
});Image Optimization
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
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:
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.js API route handler- Image2Url Service - Backend image processing service
📄 License
MIT © Image2Url
