Works with any backend npm install Zero dependencies

Declarative image-processing presets - one option, one workflow

5 built-in presets bundle the common combinations of auto-orient + resize + crop + compress. Pick one name and the entire image pipeline shapes the output for that use case. Custom specs pass through unchanged for one-off needs.

Try it: pick a preset, drop a high-res image (4000x3000 from any modern camera works great). Look at the queue's reported file size - it'll be a fraction of the original. Inspect the network request: the bytes match the preset spec.
Built-in presets
Nameauto-orientresizecompressTypical use
avatarOK512x512 contain, JPEG 0.9JPEG 0.9User profile photos
thumbnail - 320x320 contain, JPEG 0.85JPEG 0.85Gallery grid tiles
web-bannerOK1600w max (no height cap), JPEG 0.88 - Hero images, marketing cards
documentOK - JPEG 0.85Scanned forms, receipts (preserves dimensions)
social-cardOK1200x630 contain, JPEG 0.9 - OG image / Twitter Card
API
// Built-in preset by name:
new MultipleUpload('#uploader', {
 uploadUrl: '/api/upload',
 imagePreset: 'avatar'
});

// Custom spec - pass an object:
new MultipleUpload('#uploader', {
 uploadUrl: '/api/upload',
 imagePreset: {
 autoOrient: true,
 resize: { maxWidth: 2048, maxHeight: 2048, quality: 0.92, outputType: 'image/webp' },
 compress: { quality: 0.92, outputType: 'image/webp' }
 }
});

// Inspect a preset's expanded shape (useful for testing):
const spec = MultipleUpload.expandImagePreset('avatar');
console.log(spec);
// { autoOrient: true,
// resize: { maxWidth: 512, maxHeight: 512, quality: 0.9, outputType: 'image/jpeg' },
// compress: { quality: 0.9, outputType: 'image/jpeg' } }
Pipeline order

When imagePreset is set, the pipeline runs in this order:

queue file
 -> HEIC convert (if convertHeic + .heic file)
 -> imagePreset.autoOrient (EXIF rotation applied)
 -> imagePreset.resize (contain into bounds)
 -> imagePreset.crop (only for fixed-dimension custom specs)
 -> imagePreset.compress (JPEG/WebP quality)
 -> transformPipeline (user-defined transforms)
 -> ... rest of pipeline (encrypt, virus scan, ...)
Why presets instead of individual options
  • Single source of truth: 90% of teams want one of these 5 workflows. One option captures intent better than 6 individual settings.
  • Composable: imagePreset + convertHeic + imageRotate + watermark all stack - the preset doesn't disable other options.
  • Overrideable per-task: custom transforms in transformPipeline run AFTER the preset, so per-file tweaks still work.
  • Inspectable: MultipleUpload.expandImagePreset(name) returns the spec - useful for unit testing your config.
Combine with HEIC convert for the full Apple iPhone story
// User drops IMG_1234.HEIC from iPhone - output is a 512x512 JPEG avatar.
new MultipleUpload('#uploader', {
 uploadUrl: '/api/upload',
 convertHeic: true, // HEIC -> JPEG via ImageDecoder
 imagePreset: 'avatar' // 512x512 JPEG 0.9
});

// 4032x3024 HEIC (12 MP iPhone) -> 38 KB 512x512 avatar JPEG.
// All client-side. Server sees a single ready-to-use 38 KB file.