react-shrink
v1.0.0
Published
Blazing-fast client-side image compression library for React with Web Worker support. Compress images 70-99% with WebP, AVIF, JPEG. Zero dependencies, TypeScript-first.
Maintainers
Readme
React-Shrink
Blazing-fast client-side image compression library for React with Web Worker support
Compress images in the browser | Reduce file sizes by 70-99% | WebP, AVIF, JPEG support | Zero dependencies
Intelligent image compression engine for React applications. Optimize images client-side before upload with adaptive quality algorithms, Web Worker parallelization, and automatic format selection. Perfect for React, Next.js, and Vite projects.
Quick Start · API Docs · Live Examples · Benchmarks
✨ Key Features
Image Compression & Optimization:
- 🚀 Web Worker Compression - Non-blocking, parallel image processing
- 🎯 Adaptive Quality - Binary search algorithm meets exact size targets (300KB, 500KB, 1MB)
- 🖼️ Modern Image Formats - WebP, AVIF, JPEG with automatic browser fallback
- 📉 70-99% Size Reduction - Proven compression ratios from real-world testing
Developer Experience:
- 📦 Batch Image Processing - Compress multiple images concurrently
- 📱 HEIC/HEIF Support - Convert iOS images (iPhone photos)
- 🎨 PNG Transparency - Preserves alpha channels automatically
- 📊 Upload Progress Tracking - Real-time compression progress callbacks
- 🔧 TypeScript First - Complete type definitions and IntelliSense
- 🪶 5.8KB Gzipped - Minimal bundle size, zero dependencies
- ⚡ React Hooks - Simple
useShrink()hook for React integration
Production Ready:
- ✅ Works with React, Next.js, Vite, Create React App
- ✅ Browser image optimization before upload
- ✅ Automatic format detection (JPEG, PNG, WebP, AVIF, HEIC)
- ✅ Memory efficient processing
- ✅ Works on mobile browsers (iOS Safari, Chrome Mobile)
📦 Installation
Install via npm, yarn, or pnpm:
npm install react-shrink
# or
yarn add react-shrink
# or
pnpm add react-shrinkOptional: Add HEIC/HEIF support for iOS iPhone images:
npm install heic2anyCompatible with: React 16.8+, Next.js, Vite, Create React App, Remix, Gatsby
Verify installation:
npm list react-shrink🚀 Quick Start
React Hook (Recommended)
import { useShrink } from 'react-shrink'
function ImageUploader() {
const { shrink, isProcessing, progress } = useShrink()
const handleFile = async (file: File) => {
const result = await shrink(file, {
maxKB: 300, // Target 300KB or less
maxEdge: 2048 // Max 2048px width/height
})
// Upload compressed blob
await uploadToServer(result.blob)
}
return (
<div>
<input type="file" accept="image/*" onChange={e => handleFile(e.target.files[0])} />
{isProcessing && <progress value={progress} max={100} />}
</div>
)
}Standalone Function
import { shrink } from 'react-shrink'
const result = await shrink(imageFile, {
maxKB: 500,
codecs: ['webp', 'jpeg']
})
console.log(`Reduced from ${imageFile.size} to ${result.bytes} bytes`)With Web Workers (Better Performance)
import { shrinkWithWorker } from 'react-shrink'
// Non-blocking compression - UI stays responsive
const result = await shrinkWithWorker(largeFile, { maxKB: 300 })📖 API
Core Functions
shrink(file, options?)
Main compression function. Runs on main thread.
const result = await shrink(file, {
maxKB: 300, // Target max size (KB)
maxEdge: 2048, // Max dimension (px)
codecs: ['webp', 'jpeg'], // Preferred formats
quality: 0.85, // Fixed quality or auto
useWorker: false // Use web worker
})Returns: ShrinkResult
{
blob: Blob // Compressed image
bytes: number // Final size
width: number // Final dimensions
height: number
mime: string // Output format
diagnostics: {
ratio: number // Compression ratio
timeMs: number // Processing time
codec: string // Used codec
quality: number // Applied quality
}
}shrinkWithWorker(file, options?)
Worker-based compression. Recommended for large files (>2MB).
- Non-blocking UI
- 50-80% faster for large images
- Automatic worker pool management
- Same API as
shrink()
Requires: Chrome 69+, Firefox 105+, Safari 16.4+
shrinkBatch(files, options?)
Process multiple images concurrently.
const result = await shrinkBatch(files, {
maxKB: 300,
concurrency: 3, // Parallel operations
onProgress: (done, total) => console.log(`${done}/${total}`)
})
console.log(`Processed: ${result.successful}/${result.total}`)React Hook
useShrink()
Stateful compression with automatic cleanup.
const {
shrink, // Compress single file
shrinkBatch, // Compress multiple files
isProcessing, // Current state
progress, // 0-100
error, // Last error
result, // Last result
workerSupported, // Worker availability
reset // Clear state
} = useShrink()Auto-worker: Automatically uses workers for files >2MB when supported.
Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| maxKB | number | 300 | Target max file size (KB) |
| maxEdge | number | 2048 | Max width/height (px) |
| codecs | ImageCodec[] | ['webp','jpeg'] | Preferred formats |
| transparency | 'keep'┃'remove' | 'keep' | Alpha channel handling |
| quality | number | auto | Fixed quality 0-1 |
| stripMetadata | boolean | true | Remove EXIF data |
| useWorker | boolean | auto | Enable web workers |
| onProgress | function | - | Progress callback |
Presets
Ready-made configurations for common scenarios:
import { PRESET_AVATAR, PRESET_GALLERY, PRESET_SOCIAL } from 'react-shrink'
await shrink(file, PRESET_AVATAR) // 100KB, 512px
await shrink(file, PRESET_GALLERY) // 300KB, 2048px
await shrink(file, PRESET_SOCIAL) // 500KB, 1920pxAvailable presets:
PRESET_THUMBNAIL- 50KB, 200pxPRESET_AVATAR- 100KB, 512pxPRESET_GALLERY- 300KB, 2048pxPRESET_HIGH_QUALITY- 1MB, 4096pxPRESET_SOCIAL- 500KB, 1920pxPRESET_DOCUMENT- 400KB, 2560pxPRESET_ECOMMERCE- 350KB, 2000px
Custom presets:
const custom = createPreset('gallery', { maxKB: 500, codecs: ['avif', 'webp'] })💡 Examples
Batch Upload
import { useShrink } from 'react-shrink'
function BatchUploader() {
const { shrinkBatch } = useShrink()
const handleFiles = async (files: File[]) => {
const result = await shrinkBatch(files, {
maxKB: 300,
concurrency: 3,
useWorker: true
})
console.log(`Saved ${result.totalBytesSaved} bytes`)
}
return <input type="file" multiple onChange={e => handleFiles([...e.target.files])} />
}With Progress Bar
function ProgressExample() {
const [progress, setProgress] = useState(0)
const compress = async (file: File) => {
await shrink(file, {
maxKB: 300,
onProgress: setProgress
})
}
return <progress value={progress} max={100} />
}Error Handling
import { ImageLoadError, CompressionError } from 'react-shrink'
try {
const result = await shrink(file)
} catch (error) {
if (error instanceof ImageLoadError) {
alert('Invalid image file')
} else if (error instanceof CompressionError) {
alert('Compression failed')
}
}HEIC Support (iOS Photos)
import { shrink } from 'react-shrink'
// Automatic HEIC detection and conversion
const result = await shrink(iphotoFile, { maxKB: 300 })
// Works seamlessly if heic2any is installedAdvanced: Worker Pool
import { getWorkerPool, terminateWorkerPool } from 'react-shrink'
// Access worker pool directly
const pool = await getWorkerPool(4) // 4 workers
console.log(pool.getStats())
// Cleanup when done
terminateWorkerPool()🧪 Worker Support
Check browser compatibility:
import { canUseWorkerCompression, getOptimalWorkerCount } from 'react-shrink'
if (canUseWorkerCompression()) {
console.log(`Workers available: ${getOptimalWorkerCount()}`)
}Browser Requirements:
- OffscreenCanvas API
- Transferable ImageBitmap
- Worker API
Supported:
- Chrome 69+
- Firefox 105+
- Safari 16.4+
- Edge 79+
🎯 Performance
Real-World Benchmarks
Tested on MacBook Air M2 2022, Chrome browser, WebP compression with maxKB: 300:
| Resolution | Input Size | Output Size | Reduction | Time | |------------|------------|-------------|-----------|------| | 3024×4032 | 1.93 MB | 211 KB | 89% | 699ms | | 4320×4320 | 12.63 MB | 111 KB | 99% | 716ms | | 1024×1024 | 670 KB | 15.7 KB | 98% | 105ms |
🔧 Utilities
Low-level functions for advanced use:
import {
calculateNewDimensions,
hasAlphaChannel,
isCodecSupported,
getMimeType
} from 'react-shrink'
// Dimension calculations
const { width, height } = calculateNewDimensions(1920, 1080, 1024)
// Codec detection
const avifSupported = await isCodecSupported('avif')🌐 Browser Support
| Feature | Chrome | Firefox | Safari | Edge | |---------|--------|---------|--------|------| | Core | 60+ | 55+ | 12+ | 79+ | | WebP | 32+ | 65+ | 14+ | 18+ | | AVIF | 85+ | 93+ | 16+ | 93+ | | Workers | 69+ | 105+ | 16.4+ | 79+ |
🛠️ Development
# Install dependencies
npm install
# Run demo
npm run dev
# Build library
npm run build
# Run tests
npm test
# Type check
npm run typecheck
# Lint & format
npm run lint
npm run format🎯 Use Cases
Perfect for:
- 📸 Image upload forms - Reduce upload times and server bandwidth
- 🖼️ Photo galleries - Optimize multiple images before upload
- 👤 Profile picture uploads - Compress avatars and profile photos
- 🛒 E-commerce platforms - Product image optimization
- 📱 Mobile-first apps - Reduce data usage on mobile networks
- 📝 Content management systems - CMS image optimization
- 💬 Social media apps - Compress user-generated content
- 📊 File upload widgets - React file uploader with compression
License
MIT © 2025 Badla Moussaab
Contributing
Contributions are welcome! Help improve React-Shrink:
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
🌟 Support
If React-Shrink helps your project:
