Skip to content

Latest commit

 

History

History
462 lines (337 loc) · 11.9 KB

File metadata and controls

462 lines (337 loc) · 11.9 KB

React-Shrink

Blazing-fast client-side image compression library for React with Web Worker support

npm version npm downloads Bundle Size TypeScript License

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-shrink

Optional: Add HEIC/HEIF support for iOS iPhone images:

npm install heic2any

Compatible 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, 1920px

Available presets:

  • PRESET_THUMBNAIL - 50KB, 200px
  • PRESET_AVATAR - 100KB, 512px
  • PRESET_GALLERY - 300KB, 2048px
  • PRESET_HIGH_QUALITY - 1MB, 4096px
  • PRESET_SOCIAL - 500KB, 1920px
  • PRESET_DOCUMENT - 400KB, 2560px
  • PRESET_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 installed

Advanced: 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:

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

🌟 Support

If React-Shrink helps your project:

🔗 Links