Compressor.js: client-side image compression before upload
JavaScript image compressor.
At a glance
- What is it?
- Compressor.js wraps the browser's canvas toBlob() call in a small API for shrinking images in the browser before they hit your upload endpoint. It is convenient, it is lossy, and it behaves differently in every browser.
- Who is it for?
- Adopt Compressor.js when you want to cut upload payloads on the client and you accept that the browser decides the final bytes. Do not adopt it when you need reproducible output, server-side control over encoding, or preserved EXIF metadata by default.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 17 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The upload problem Compressor.js actually solves
A phone camera produces JPEGs that are routinely several megabytes. Sending those straight to an upload endpoint costs bandwidth on the client, storage on the server, and time for the user. Compressor.js exists to move that reduction to the browser: the README describes its purpose as precompressing an image on the client side before uploading it. The target user is a front-end developer who already has a file input and an upload call and wants to insert one step between them. It is not an image processing library in the general sense. There is no server component, no CLI, no build-time pipeline. If your images arrive from an API or a backend job rather than a user's file picker, this library has nothing to offer you.
How the compression actually happens
The mechanism is short enough to state plainly. The library draws the input file onto an HTMLCanvasElement, optionally resizing it, then calls the browser's native HTMLCanvasElement.toBlob() method to produce the output. The README is explicit about the consequences of that choice: the compression is lossy, asynchronous, and has different compression effects in different browsers. That last point is the one worth internalizing. Because the encoder belongs to the browser, not to the library, Chrome, Firefox and Safari can produce different byte sizes and slightly different visual results from identical input and identical options. The library ships as UMD, CommonJS and ES Module builds under dist/, and package.json points main at dist/compressor.common.js, module at dist/compressor.esm.js and browser at dist/compressor.js, with type declarations in types/index.d.ts. The runtime dependencies are blueimp-canvas-to-blob and is-blob, the first of which exists because toBlob() is not universally available. The asynchronous nature is not an implementation detail you can ignore: the result is only reachable inside the success hook.
Installing Compressor.js and compressing a first file
Installation is a single npm command, as the README shows in its Install section. The package name is compressorjs.
npm install compressorjsThe README's example pairs a file input with a change listener and constructs a new Compressor with a quality option. Because the work is asynchronous, the compressed result arrives in the success callback rather than as a return value, and the example then posts it as multipart form data. Note the third argument to formData.append: the README comments that it is required by the server and falls back to a literal filename when the Blob has no name.
import Compressor from 'compressorjs';
document.getElementById('file').addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) {
return;
}
new Compressor(file, {
quality: 0.6,
success(result) {
const formData = new FormData();
formData.append('file', result, result.name || 'compressed-image.jpg');
fetch('/path/to/upload', { method: 'POST', body: formData });
},
error(err) {
console.log(err.message);
},
});
});The markup side is a plain file input with an image accept filter, which is what the README uses to drive the example.
<input type="file" id="file" accept="image/*">Defaults can be changed globally rather than per call: the README documents Compressor.setDefaults(options) for that. On the options themselves, the README's own tips suggest quality between 0.6 and 0.8 as a balance between file size and visual quality, and warn against quality: 1 unless you want the output to stay close to the original, because it can increase the file size rather than reduce it.
The strict option and other guardrails against bigger output
A compressor that occasionally makes files larger is a real problem, and the library's answer is the strict option, which defaults to true. When the compressed image is larger than the original, strict makes the library return the original instead. The README lists the exceptions where that fallback does not apply, and the list is long: retainExif set to true, a mimeType option that differs from the source image's type, a width or height option larger than the natural dimensions, a minWidth or minHeight larger than the natural dimensions, or a maxWidth or maxHeight smaller than the natural dimensions. In other words, the moment you ask for a type conversion, an upscale, or a downscale that the guardrails would otherwise undo, you have opted out of the size protection. The README flags convertTypes and convertSize as the way to automatically convert large PNG files to JPEG to reduce file size, which is exactly the case where strict will not save you from a larger result. If you enable that conversion, measure the output yourself.
EXIF orientation, retainExif, and the metadata you lose
checkOrientation defaults to true and reads the JPEG Exif Orientation value to rotate or flip the image automatically. The README attaches two warnings to it. First, some JPEGs carry incorrect or non-standard Orientation values, so the automatic correction cannot be trusted in every case. Second, images larger than roughly 10 MB should have this option disabled to avoid an out-of-memory crash. That second warning is the more consequential one: the same section that tells you to set maxWidth and maxHeight for very large images is telling you that orientation handling itself can exhaust memory. There is also a metadata consequence. The README states that the image's Exif information is removed after compression, so if you need that data you may have to upload the original as well. retainExif defaults to false and can be set to true to keep Exif information, but the README does not document what happens to orientation handling when retainExif is enabled, and it does not document rollback if a compression run fails midway. Treat retainExif as a flag to test against your own files rather than one to assume.
Where Compressor.js is the wrong tool
The browser dependency is the boundary. Because output depends on the host's canvas encoder, you cannot guarantee that the same input produces the same bytes across browsers or across browser versions, which rules out use cases where the compressed artefact is itself the deliverable, such as a signed asset or a reproducible build. Server-side encoding, where the encoder version is pinned, is the better fit there. The library also does nothing about the network beyond making the payload smaller: there is no chunking, no resumable upload, no retry logic. And it is not a general-purpose image pipeline. It works on File and Blob inputs from the browser, so batch processing, format conversion at scale, and thumbnail generation for stored assets all belong somewhere else. Finally, the README's own advice to disable checkOrientation above roughly 10 MB means the largest images, the ones where compression would help most, are the ones most likely to need options that reduce what the library can safely do.
How it compares with browser-image-compression and Pica
The closest alternative in the same space is browser-image-compression, which also runs in the browser and also targets the pre-upload case. The practical difference is in the API surface: Compressor.js exposes a constructor with hooks (success, error) and a documented set of options including strict, checkOrientation, retainExif, quality, the width and height family, and resize with its none, contain and cover modes. The resize option only applies when both width and height are specified, which is a constraint worth knowing before you reach for it. Pica is a different animal: it is oriented around resizing quality in the browser rather than around the upload-preparation workflow, so choosing between them is really a question of whether you need a resize engine or a pre-upload step. The README for Compressor.js does not benchmark itself against either, so any comparison of output quality between the three would have to come from your own files, not from the documentation.
Maintenance, licence and the cost of staying current
The repository is not archived, and the last push was on 2026-09-13. The most recent release is v1.3.0 from 2026-04-06, following v1.2.1 in February 2023 and v1.2.0 earlier that same month. That gap between v1.2.1 and v1.3.0 is the honest signal about upgrade cadence: this is a small library that moves when something needs fixing, not on a schedule. The upside of that is a small dependency surface, blueimp-canvas-to-blob and is-blob, and no transitive framework to track. The cost is that a browser change to canvas encoding behaviour may sit unaddressed until someone files it. The licence is MIT, declared in package.json and present as a LICENSE file at the repository root. MIT is permissive and imposes no copyleft obligation on your application, but the library bundles blueimp-canvas-to-blob, which carries its own licence terms; check that file if your organisation has a policy on transitive dependencies. Nothing here is legal advice, and the LICENSE file is the authority.
Editorial conclusion
Adopt Compressor.js when you want to cut upload payloads on the client and you accept that the browser decides the final bytes. Do not adopt it when you need reproducible output, server-side control over encoding, or preserved EXIF metadata by default. Before shipping, verify how your target browsers handle quality: 0.6 on large JPEGs, confirm that maxWidth and maxHeight are set to 4096 or lower for oversized photos, and check whether your upload endpoint can accept a Blob whose name may be missing, which is why the README passes result.name || 'compressed-image.jpg' as the third FormData argument.
Frequently asked questions
How do I install Compressor.js?
Install it from npm with the package name compressorjs, as shown in the README's Install section. The package publishes UMD, CommonJS and ES Module builds under dist/ and ships TypeScript declarations in types/index.d.ts.
How can I compress images using JavaScript with Compressor.js?
Construct a new Compressor with a File or Blob and an options object, then read the compressed result inside the success hook, because the README states the process is asynchronous. The README's example passes quality: 0.6 and posts the result as FormData.
How can I compress an image in HTML with Compressor.js?
The README's example uses an input element with type file and an accept attribute of image/*, then listens for the change event and passes the selected file to new Compressor. The compressed result is only available in the success hook, from which the example builds a FormData object and posts it.
How can I reduce the MB size of an image with Compressor.js?
The README's tips suggest a quality value between 0.6 and 0.8 for a balance between file size and visual quality, and warn that quality: 1 can increase the file size instead of reducing it. For very large images it also advises setting maxWidth and maxHeight to limit the canvas size and avoid browser memory issues.
compressorjs vs browser image compression: what is the difference?
Both run in the browser and target pre-upload compression, so the choice comes down to API and options rather than to a different execution model. Compressor.js documents a constructor with success and error hooks and options such as strict, checkOrientation, retainExif, quality and resize; the README does not compare the two libraries or publish output measurements.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fengyuanchen-compressorjs)