browser-image-compression and compressorjs are both JavaScript libraries designed to compress images directly in the browser before uploading or displaying them. They help reduce bandwidth usage, improve upload speeds, and optimize storage by resizing and re-encoding images using canvas-based techniques. Both support common image formats like JPEG and PNG, and allow developers to control quality, dimensions, and file size constraints through configuration options.
Both browser-image-compression and compressorjs let you shrink images in the browser using the <canvas> API—avoiding server round trips and saving bandwidth. But they differ in how they handle sizing, file limits, and developer ergonomics. Let’s compare them head-to-head.
browser-image-compression lets you set both pixel dimensions and a target file size. If the output exceeds your file limit, it automatically retries with lower quality until it fits.
import imageCompression from 'browser-image-compression';
const options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true
};
const compressedFile = await imageCompression(file, options);
// Result is guaranteed ≤ 1MB
compressorjs focuses on pixel dimensions and quality percentage, but doesn’t enforce file size limits. You can check the result size afterward, but there’s no auto-adjustment.
import Compressor from 'compressorjs';
new Compressor(file, {
quality: 0.6,
maxWidth: 1920,
success(result) {
console.log('Size:', result.size); // May still be >1MB
}
});
💡 Use
browser-image-compressionwhen your backend enforces strict file size limits (e.g., “max 2MB”). Usecompressorjswhen you only care about visual quality and dimensions.
browser-image-compression uses async/await with promises, fitting naturally into modern JavaScript workflows.
try {
const output = await imageCompression(inputFile, options);
uploadToServer(output);
} catch (error) {
handleError(error);
}
compressorjs relies on callbacks (success, error), which can lead to nested logic in complex flows.
new Compressor(file, {
success(result) {
uploadToServer(result);
},
error(err) {
handleError(err);
}
});
💡 If you’re already using async/await heavily,
browser-image-compressionreduces mental overhead. If you’re in a callback-heavy codebase (e.g., legacy jQuery apps),compressorjsmay feel more familiar.
browser-image-compression preserves aspect ratio by default when you set maxWidthOrHeight. It doesn’t support cropping—you get the full scaled image.
// Scales proportionally to fit within 800px
const options = { maxWidthOrHeight: 800 };
compressorjs also preserves aspect ratio by default, but offers an aspectRatio option to force specific ratios (e.g., 16:9). However, it doesn’t crop—it just scales to fit the bounding box.
// Scales to fit within 800px width, then forces height to match 16:9
new Compressor(file, {
maxWidth: 800,
aspectRatio: 16 / 9
});
⚠️ Neither library performs true cropping (i.e., cutting out parts of the image). For that, you’d need additional tools like
cropperjs.
browser-image-compression exposes detailed control over:
useWebWorker)mimeType)initialQuality, maxIteration)But it doesn’t return metadata about the compression process.
compressorjs provides less granular control but includes before/after file stats in the success callback:
new Compressor(file, {
success(result, { size, naturalWidth, naturalHeight }) {
console.log(`Compressed from ${file.size} to ${result.size}`);
}
});
This makes it easier to show users how much space they saved.
Both libraries rely on the HTML5 Canvas API and Blob support, so they work in all modern browsers (IE10+ for compressorjs, IE11+ for browser-image-compression due to Promise usage).
browser-image-compression throws errors as exceptions, which you catch with try/catch.
compressorjs calls the error callback for issues like invalid files or unsupported formats.
You need avatars ≤ 500KB and ≤ 400×400px.
browser-image-compressionconst compressed = await imageCompression(file, {
maxSizeMB: 0.5,
maxWidthOrHeight: 400
});
Users upload vacation photos; you want to show “Saved 2.1MB!” after compression.
compressorjsnew Compressor(file, {
quality: 0.7,
success(result, { size }) {
showSavings(file.size - size);
}
});
A job application form requires three document scans (max 1MB each).
browser-image-compressionconst [id, resume, cert] = await Promise.all([
imageCompression(idFile, opts),
imageCompression(resumeFile, opts),
imageCompression(certFile, opts)
]);
| Feature | browser-image-compression | compressorjs |
|---|---|---|
| File size enforcement | ✅ Auto-adjusts quality to fit | ❌ Manual check only |
| API style | ✅ Promise/async-await | ❌ Callbacks |
| Aspect ratio control | ❌ Proportional scaling only | ✅ Force ratio (no crop) |
| Compression metadata | ❌ Not provided | ✅ Original vs compressed |
| Web Worker support | ✅ Optional | ❌ Not supported |
browser-image-compressioncompressorjsBoth are actively maintained and production-ready. Choose based on whether your priority is enforcing constraints (browser-image-compression) or simplicity with feedback (compressorjs).
Choose browser-image-compression if you need fine-grained control over file size limits (e.g., 'max 1MB') and want automatic scaling based on both dimensions and file size. It’s well-suited for forms where users must meet strict upload requirements, and its promise-based API integrates cleanly with modern async/await patterns.
Choose compressorjs if you prefer a more concise API with built-in support for maintaining aspect ratio via aspectRatio and need quick access to metadata like original vs compressed file size. Its callback-style interface works well in event-driven workflows, and it provides straightforward options for basic compression tasks without complex file-size targeting.
Javascript module to be run in the web browser for image compression.
open https://donaldcwl.github.io/browser-image-compression/example/basic.html
or check the "example" folder in this repo
<input type="file" accept="image/*" onchange="handleImageUpload(event);">
async function handleImageUpload(event) {
const imageFile = event.target.files[0];
console.log('originalFile instanceof Blob', imageFile instanceof Blob); // true
console.log(`originalFile size ${imageFile.size / 1024 / 1024} MB`);
const options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true,
}
try {
const compressedFile = await imageCompression(imageFile, options);
console.log('compressedFile instanceof Blob', compressedFile instanceof Blob); // true
console.log(`compressedFile size ${compressedFile.size / 1024 / 1024} MB`); // smaller than maxSizeMB
await uploadToServer(compressedFile); // write your own logic
} catch (error) {
console.log(error);
}
}
function handleImageUpload(event) {
var imageFile = event.target.files[0];
console.log('originalFile instanceof Blob', imageFile instanceof Blob); // true
console.log(`originalFile size ${imageFile.size / 1024 / 1024} MB`);
var options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true
}
imageCompression(imageFile, options)
.then(function (compressedFile) {
console.log('compressedFile instanceof Blob', compressedFile instanceof Blob); // true
console.log(`compressedFile size ${compressedFile.size / 1024 / 1024} MB`); // smaller than maxSizeMB
return uploadToServer(compressedFile); // write your own logic
})
.catch(function (error) {
console.log(error.message);
});
}
You can install it via npm or yarn
npm install browser-image-compression --save
# or
yarn add browser-image-compression
import imageCompression from 'browser-image-compression';
(can be used in frameworks like React, Angular, Vue etc)
(work with bundlers like webpack and rollup)
You can download imageCompression from the dist folder.
Alternatively, you can use a CDN like delivrjs:
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.1/dist/browser-image-compression.js"></script>
If this project helps you reduce the time to develop, you can buy me a cup of coffee :)
(powered by Stripe)
// you should provide one of maxSizeMB, maxWidthOrHeight in the options
const options: Options = {
maxSizeMB: number, // (default: Number.POSITIVE_INFINITY)
maxWidthOrHeight: number, // compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)
// but, automatically reduce the size to smaller than the maximum Canvas size supported by each browser.
// Please check the Caveat part for details.
onProgress: Function, // optional, a function takes one progress argument (percentage from 0 to 100)
useWebWorker: boolean, // optional, use multi-thread web worker, fallback to run in main-thread (default: true)
libURL: string, // optional, the libURL of this library for importing script in Web Worker (default: https://cdn.jsdelivr.net/npm/browser-image-compression/dist/browser-image-compression.js)
preserveExif: boolean, // optional, use preserve Exif metadata for JPEG image e.g., Camera model, Focal length, etc (default: false)
signal: AbortSignal, // optional, to abort / cancel the compression
// following options are for advanced users
maxIteration: number, // optional, max number of iteration to compress the image (default: 10)
exifOrientation: number, // optional, see https://stackoverflow.com/a/32490603/10395024
fileType: string, // optional, fileType override e.g., 'image/jpeg', 'image/png' (default: file.type)
initialQuality: number, // optional, initial quality value between 0 and 1 (default: 1)
alwaysKeepResolution: boolean // optional, only reduce quality, always keep width and height (default: false)
}
imageCompression(file: File, options: Options): Promise<File>
Each browser limits the maximum size of a browser Canvas object.
So, we resize the image to less than the maximum size that each browser restricts.
(However, the proportion/ratio of the image remains.)
To use this feature, please check the browser compatibility: https://caniuse.com/?search=AbortController
function handleImageUpload(event) {
var imageFile = event.target.files[0];
var controller = new AbortController();
var options = {
// other options here
signal: controller.signal,
}
imageCompression(imageFile, options)
.then(function (compressedFile) {
return uploadToServer(compressedFile); // write your own logic
})
.catch(function (error) {
console.log(error.message); // output: I just want to stop
});
// simulate abort the compression after 1.5 seconds
setTimeout(function () {
controller.abort(new Error('I just want to stop'));
}, 1500);
}
imageCompression.getDataUrlFromFile(file: File): Promise<base64 encoded string>
imageCompression.getFilefromDataUrl(dataUrl: string, filename: string, lastModified?: number): Promise<File>
imageCompression.loadImage(url: string): Promise<HTMLImageElement>
imageCompression.drawImageInCanvas(img: HTMLImageElement, fileType?: string): HTMLCanvasElement | OffscreenCanvas
imageCompression.drawFileInCanvas(file: File, options?: Options): Promise<[ImageBitmap | HTMLImageElement, HTMLCanvasElement | OffscreenCanvas]>
imageCompression.canvasToFile(canvas: HTMLCanvasElement | OffscreenCanvas, fileType: string, fileName: string, fileLastModified: number, quality?: number): Promise<File>
imageCompression.getExifOrientation(file: File): Promise<number> // based on https://stackoverflow.com/a/32490603/10395024
imageCompression.copyExifWithoutOrientation(copyExifFromFile: File, copyExifToFile: File): Promise<File> // based on https://gist.github.com/tonytonyjan/ffb7cd0e82cb293b843ece7e79364233
![]() IE / Edge | ![]() Firefox | ![]() Chrome | ![]() Safari | ![]() iOS Safari | ![]() Opera |
|---|---|---|---|---|---|
| IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
This library uses ES features such as Promise API, globalThis. If you need to support browsers that do not support new ES features like IE. You can include the core-js polyfill in your project.
You can include the following script to load the core-js polyfill:
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/3.21.1/minified.min.js"></script>
The webp compression is supported on major browsers. Please see https://caniuse.com/mdn-api_offscreencanvas_converttoblob_option_type_parameter_webp for browser compatibility.
The browser needs to support "OffscreenCanvas" API in order to take advantage of non-blocking compression. If the browser does not support "OffscreenCanvas" API, the main thread is used instead. See https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas#browser_compatibility for browser compatibility of "OffscreenCanvas" API.
Typescript definitions are included in the package & referenced in the types section of the package.json
If your website has CSP enabled and you want to use Web Worker (useWebWorker: true), please add the following to the response header
content-security-policy: script-src 'self' blob: https://cdn.jsdelivr.net
blob: is for loading Web Worker scripthttps://cdn.jsdelivr.net is for importing this library from CDN inside Web Worker script. If you don't want to load this library from CDN, you can set your self hosted library URL in options.libURL.npm run watch # it will watch code change in lib/ folder and generate js in dist/ foldernpm run test