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
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)
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-shrinkimport { 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>
)
}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`)import { shrinkWithWorker } from 'react-shrink'
// Non-blocking compression - UI stays responsive
const result = await shrinkWithWorker(largeFile, { maxKB: 300 })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
}
}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+
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}`)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.
| 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 |
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'] })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])} />
}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} />
}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')
}
}import { shrink } from 'react-shrink'
// Automatic HEIC detection and conversion
const result = await shrink(iphotoFile, { maxKB: 300 })
// Works seamlessly if heic2any is installedimport { getWorkerPool, terminateWorkerPool } from 'react-shrink'
// Access worker pool directly
const pool = await getWorkerPool(4) // 4 workers
console.log(pool.getStats())
// Cleanup when done
terminateWorkerPool()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+
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 |
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')| 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+ |
# 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 formatPerfect 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
MIT Β© 2025 Badla Moussaab
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
If React-Shrink helps your project:
- Star the repository on GitHub
- Report bugs and issues
- π¦ npm Package
- π GitHub Repository
- π Documentation
- π Issue Tracker
- π Bundle Size Analysis