compressorjs is a standalone JavaScript library designed for compressing and converting images directly in the browser using the Canvas API. It provides a flexible API for resizing and optimizing image files before they are uploaded or stored. @uppy/compressor, on the other hand, is a plugin for the Uppy file uploader framework that integrates image compression capabilities into the upload pipeline. It leverages compression logic (often powered by compressorjs internally) to automatically process files before they are sent to the server, specifically tailored for workflows already using Uppy.
Reducing image file size before upload is a critical optimization for modern web applications. It saves bandwidth, speeds up transfers, and reduces server storage costs. Two common solutions in the JavaScript ecosystem are compressorjs and @uppy/compressor. While they share the same goal, they serve different architectural roles. Let's explore how they differ in practice.
compressorjs is a standalone library.
fetch, XMLHttpRequest, or any custom logic.// compressorjs: Standalone usage
import { Compressor } from 'compressorjs';
const file = input.files[0];
new Compressor(file, {
quality: 0.6,
success(result) {
// result is a Blob, ready to upload manually
const formData = new FormData();
formData.append('file', result);
fetch('/upload', { method: 'POST', body: formData });
},
});
@uppy/compressor is a plugin for the Uppy uploader.
@uppy/core to function.// @uppy/compressor: Plugin usage
import Uppy from '@uppy/core';
import Compressor from '@uppy/compressor';
const uppy = new Uppy()
.use(Compressor, {
quality: 0.6,
// Automatically runs before upload
});
// Uppy handles the upload after compression
uppy.upload();
compressorjs exposes detailed options for image manipulation.
width, height, resize, and mimeType directly.success, error) handle the flow explicitly.// compressorjs: Detailed configuration
new Compressor(file, {
width: 800,
height: 600,
resize: 'cover',
mimeType: 'image/webp',
success(result) {
console.log('Compressed to WebP:', result.size);
},
error(err) {
console.error('Compression failed:', err.message);
},
});
@uppy/compressor simplifies configuration for upload flows.
// @uppy/compressor: Simplified configuration
uppy.use(Compressor, {
quality: 0.6,
mimeType: 'image/webp',
// Uppy handles errors via its own logger/events
});
uppy.on('upload-success', (file, response) => {
console.log('Upload complete after compression');
});
compressorjs returns a new Blob object.
// compressorjs: Manual file handling
new Compressor(file, {
success(result) {
// You must create a new File or Blob to upload
const compressedFile = new File([result], file.name, {
type: result.type,
lastModified: Date.now(),
});
// Proceed with your own upload logic
},
});
@uppy/compressor replaces the file in Uppy's state.
// @uppy/compressor: Automatic file replacement
// No need to manually swap files.
// The plugin intercepts the file before the request.
uppy.use(Compressor, {
quality: 0.6,
});
// When uppy.upload() is called, it sends the compressed file
compressorjs requires explicit error callbacks.
// compressorjs: Explicit error handling
new Compressor(file, {
error(err) {
// Decide whether to upload original or show error
alert('Compression failed, uploading original.');
uploadOriginal(file);
},
});
@uppy/compressor integrates with Uppy's error system.
upload-error or plugin events.// @uppy/compressor: Integrated error handling
uppy.on('upload-error', (file, error) => {
// Handles both network and compression errors uniformly
console.error('Upload or compression failed:', error);
});
These tools are specific to image optimization. Consider alternatives when:
| Feature | compressorjs | @uppy/compressor |
|---|---|---|
| Type | π οΈ Standalone Library | π Uppy Plugin |
| Dependency | β None | β Requires @uppy/core |
| File Output | π Returns Blob (Manual handling) | π Auto-replaces in Uppy state |
| Configuration | βοΈ Detailed API options | βοΈ Plugin options |
| Error Handling | β οΈ Callback-based | π’ Uppy Event System |
| Best For | π Custom upload logic | π§© Uppy-based upload flows |
Think in terms of your upload architecture:
compressorjs. It is lightweight, flexible, and does not force an upload framework on you.@uppy/compressor. It saves development time by integrating directly into the existing pipeline, ensuring files are optimized without extra wiring.Final Thought: Both tools solve the same problem but at different layers of the stack. compressorjs is the engine; @uppy/compressor is the integration of that engine into a specific vehicle. Choose based on whether you need the engine alone or the complete vehicle.
Choose compressorjs if you need a standalone image compression utility that works independently of any file uploader. It is ideal for custom upload implementations, local file processing before storage, or when you want to avoid tying your compression logic to a specific upload framework. This gives you full control over when and how compression happens without importing a heavy uploader library.
Choose @uppy/compressor if your project already uses the Uppy file uploader framework. It integrates compression directly into the upload pipeline, handling file replacement automatically before the request is sent, which reduces boilerplate code for Uppy users. This is the most efficient path if you want seamless compression without manually wiring up file processing events.
JavaScript image compressor. Uses the Browser's native canvas.toBlob API to do the compression work, which means it is lossy compression, asynchronous, and has different compression effects in different browsers. Generally use this to precompress a image on the client side before uploading it.
dist/
βββ compressor.js (UMD)
βββ compressor.min.js (UMD, compressed)
βββ compressor.common.js (CommonJS, default)
βββ compressor.esm.js (ES Module)
npm install compressorjs
new Compressor(file[, options])
file
The target image file for compressing.
options
ObjectThe options for compressing. Check out the available options.
<input type="file" id="file" accept="image/*">
import axios from 'axios';
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,
// The compression process is asynchronous,
// which means you have to access the `result` in the `success` hook function.
success(result) {
const formData = new FormData();
// The third parameter is required for server
formData.append('file', result, result.name);
// Send the compressed image file to server with XMLHttpRequest.
axios.post('/path/to/upload', formData).then(() => {
console.log('Upload success');
});
},
error(err) {
console.log(err.message);
},
});
});
You may set compressor options with new Compressor(file, options).
If you want to change the global default options, You may use Compressor.setDefaults(options).
booleantrueIndicates whether to output the original image instead of the compressed one when the size of the compressed image is greater than the original one's, except the following cases:
retainExif option is set to true.mimeType option is set and its value is different from the mime type of the image.width option is set and its value is greater than the natural width of the image.height option is set and its value is greater than the natural height of the image.minWidth option is set and its value is greater than the natural width of the image.minHeight option is set and its value is greater than the natural height of the image.maxWidth option is set and its value is less than the natural width of the image.maxHeight option is set and its value is less than the natural height of the image.booleantrueIndicates whether to read the image's Exif Orientation value (JPEG image only), and then rotate or flip the image automatically with the value.
Notes:
booleanfalseIndicates whether to retain the image's Exif information after compressed.
numberInfinityThe max-width of the output image. The value should be greater than 0.
Avoid getting a blank output image, you might need to set the
maxWidthandmaxHeightoptions to limited numbers, because of the size limits of a canvas element, recommend to use4096or lesser.
numberInfinityThe max height of the output image. The value should be greater than 0.
number0The min-width of the output image. The value should be greater than 0 and should not be greater than the maxWidth.
number0The min-height of the output image. The value should be greater than 0 and should not be greater than the maxHeight.
numberundefinedThe width of the output image. If not specified, the natural width of the original image will be used, or if the height option is set, the width will be computed automatically by the natural aspect ratio.
numberundefinedThe height of the output image. If not specified, the natural height of the original image will be used, or if the width option is set, the height will be computed automatically by the natural aspect ratio.
string"none""none", "contain", and "cover".Sets how the size of the image should be resized to the container specified by the width and height options.
Note: This option only available when both the width and height options are specified.
number0.8The quality of the output image. It must be a number between 0 and 1. If this argument is anything else, the default values 0.92 and 0.80 are used for image/jpeg and image/webp respectively. Other arguments are ignored. Be careful to use 1 as it may make the size of the output image become larger.
Note: This option only available for image/jpeg and image/webp images.
Check out canvas.toBlob for more detail.
Examples:
| Quality | Input size | Output size | Compression ratio | Description |
|---|---|---|---|---|
| 0 | 2.12 MB | 114.61 KB | 94.72% | - |
| 0.2 | 2.12 MB | 349.57 KB | 83.90% | - |
| 0.4 | 2.12 MB | 517.10 KB | 76.18% | - |
| 0.6 | 2.12 MB | 694.99 KB | 67.99% | Recommend |
| 0.8 | 2.12 MB | 1.14 MB | 46.41% | Recommend |
| 1 | 2.12 MB | 2.12 MB | 0% | Not recommend |
| NaN | 2.12 MB | 2.01 MB | 5.02% | - |
string'auto'The mime type of the output image. By default, the original mime type of the source image file will be used.
Array or string (multiple types should be separated by commas)['image/png']['image/png', 'image/webp']'image/png,image/webp'Files whose file type is included in this list, and whose file size exceeds the convertSize value will be converted to JPEGs.
number5000000 (5 MB)Files whose file type is included in the convertTypes list, and whose file size exceeds this value will be converted to JPEGs. To disable this, just set the value to Infinity.
Examples:
| convertSize | Input size (type) | Output size (type) | Compression ratio |
|---|---|---|---|
| 5 MB | 1.87 MB (PNG) | 1.87 MB (PNG) | 0% |
| 5 MB | 5.66 MB (PNG) | 450.24 KB (JPEG) | 92.23% |
| 5 MB | 9.74 MB (PNG) | 883.89 KB (JPEG) | 91.14% |
Functionnullcontext: The 2d rendering context of the canvas.canvas: The canvas for compression.The hook function to execute before drawing the image into the canvas for compression.
new Compressor(file, {
beforeDraw(context, canvas) {
context.fillStyle = '#fff';
context.fillRect(0, 0, canvas.width, canvas.height);
context.filter = 'grayscale(100%)';
},
});
Functionnullcontext: The 2d rendering context of the canvas.canvas: The canvas for compression.The hook function to execute after drawing the image into the canvas for compression.
new Compressor(file, {
drew(context, canvas) {
context.fillStyle = '#fff';
context.font = '2rem serif';
context.fillText('watermark', 20, canvas.height - 20);
},
});
Functionnullresult: The compressed image (a File (read only) or Blob object).The hook function to execute when successful to compress the image.
Functionnullerr: The compression error (an Error object).The hook function executes when fails to compress the image.
Abort the compression process.
const compressor = new Compressor(file);
// Do something...
compressor.abort();
If you have to use another compressor with the same namespace, just call the Compressor.noConflict static method to revert to it.
<script src="other-compressor.js"></script>
<script src="compressor.js"></script>
<script>
Compressor.noConflict();
// Code that uses other `Compressor` can follow here.
</script>
Please read through our contributing guidelines.
Maintained under the Semantic Versioning guidelines.
MIT Β© Chen Fengyuan