compressorjs vs @uppy/compressor
Client-Side Image Compression Strategies in Frontend Applications
compressorjs@uppy/compressor

Client-Side Image Compression Strategies in Frontend Applications

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.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
compressorjs250,8275,729154 kB123 years agoMIT
@uppy/compressor39,02930,68127 kB2053 months agoMIT

Client-Side Image Compression: @uppy/compressor vs compressorjs

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.

πŸ—οΈ Architecture & Dependency: Standalone vs Plugin

compressorjs is a standalone library.

  • It has no dependencies on file uploaders.
  • You can use it with fetch, XMLHttpRequest, or any custom logic.
  • Best for projects that need compression without a full upload framework.
// 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.

  • It depends on @uppy/core to function.
  • It hooks into Uppy's lifecycle events automatically.
  • Best for projects already committed to the Uppy ecosystem.
// @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();

βš™οΈ API Configuration & Control

compressorjs exposes detailed options for image manipulation.

  • You control width, height, resize, and mimeType directly.
  • Callbacks (success, error) handle the flow explicitly.
  • Suitable for complex preprocessing logic beyond just uploading.
// 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.

  • Options are passed during plugin installation.
  • It abstracts away the success/error callbacks into Uppy's event system.
  • Less flexible if you need to manipulate the file outside the upload context.
// @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');
});

πŸ“„ File Handling & Output

compressorjs returns a new Blob object.

  • You are responsible for replacing the original file.
  • You must manually manage form data or request bodies.
  • Gives you the freedom to store the blob locally or send it anywhere.
// 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.

  • The original file in the Uppy store is swapped for the compressed version.
  • No manual form data creation is needed.
  • Ensures the uploader sends the optimized version without extra code.
// @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

⚠️ Error Handling & Resilience

compressorjs requires explicit error callbacks.

  • You must define what happens if compression fails (e.g., fallback to original).
  • Gives you granular control over retry logic or user notifications.
  • Essential for robust standalone implementations.
// 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.

  • Errors are emitted through Uppy's upload-error or plugin events.
  • Less boilerplate but less control over specific compression failures.
  • Relies on Uppy's built-in retry mechanisms.
// @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);
});

🌱 When Not to Use These

These tools are specific to image optimization. Consider alternatives when:

  • You need server-side compression for security or consistency: Use backend libraries (e.g., Sharp for Node.js).
  • You are handling video or non-image files: These packages are strictly for images.
  • You need advanced format conversion (e.g., HEIC to JPEG): Ensure the library supports the specific input format, as browser Canvas API has limitations.

πŸ“Œ Summary Table

Featurecompressorjs@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

πŸ’‘ Final Recommendation

Think in terms of your upload architecture:

  • Building a custom uploader or just processing files? β†’ Use compressorjs. It is lightweight, flexible, and does not force an upload framework on you.
  • Already using Uppy? β†’ Use @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.

How to Choose: compressorjs vs @uppy/compressor

  • compressorjs:

    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.

  • @uppy/compressor:

    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.

README for compressorjs

Compressor.js

Coverage Status Downloads Version Gzip Size

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.

Table of contents

Main

dist/
β”œβ”€β”€ compressor.js        (UMD)
β”œβ”€β”€ compressor.min.js    (UMD, compressed)
β”œβ”€β”€ compressor.common.js (CommonJS, default)
└── compressor.esm.js    (ES Module)

Getting started

Install

npm install compressorjs

Usage

Syntax

new Compressor(file[, options])

file

The target image file for compressing.

options

  • Type: Object
  • Optional

The options for compressing. Check out the available options.

Example

<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);
    },
  });

});

⬆ back to top

Options

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).

strict

  • Type: boolean
  • Default: true

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

  • The retainExif option is set to true.
  • The mimeType option is set and its value is different from the mime type of the image.
  • The width option is set and its value is greater than the natural width of the image.
  • The height option is set and its value is greater than the natural height of the image.
  • The minWidth option is set and its value is greater than the natural width of the image.
  • The minHeight option is set and its value is greater than the natural height of the image.
  • The maxWidth option is set and its value is less than the natural width of the image.
  • The maxHeight option is set and its value is less than the natural height of the image.

checkOrientation

  • Type: boolean
  • Default: true

Indicates whether to read the image's Exif Orientation value (JPEG image only), and then rotate or flip the image automatically with the value.

Notes:

  • Don't trust this all the time as some JPEG images have incorrect (not standard) Orientation values.
  • If the size of the target image is too large (e.g., greater than 10 MB), you should disable this option to avoid an out-of-memory crash.
  • The image's Exif information will be removed after compressed, so if you need the Exif information, you may need to upload the original image as well.

retainExif

  • Type: boolean
  • Default: false

Indicates whether to retain the image's Exif information after compressed.

maxWidth

  • Type: number
  • Default: Infinity

The 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 maxWidth and maxHeight options to limited numbers, because of the size limits of a canvas element, recommend to use 4096 or lesser.

maxHeight

  • Type: number
  • Default: Infinity

The max height of the output image. The value should be greater than 0.

minWidth

  • Type: number
  • Default: 0

The min-width of the output image. The value should be greater than 0 and should not be greater than the maxWidth.

minHeight

  • Type: number
  • Default: 0

The min-height of the output image. The value should be greater than 0 and should not be greater than the maxHeight.

width

  • Type: number
  • Default: undefined

The 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.

height

  • Type: number
  • Default: undefined

The 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.

resize

  • Type: string
  • Default: "none"
  • Options: "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.

quality

  • Type: number
  • Default: 0.8

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

QualityInput sizeOutput sizeCompression ratioDescription
02.12 MB114.61 KB94.72%-
0.22.12 MB349.57 KB83.90%-
0.42.12 MB517.10 KB76.18%-
0.62.12 MB694.99 KB67.99%Recommend
0.82.12 MB1.14 MB46.41%Recommend
12.12 MB2.12 MB0%Not recommend
NaN2.12 MB2.01 MB5.02%-

mimeType

  • Type: string
  • Default: 'auto'

The mime type of the output image. By default, the original mime type of the source image file will be used.

convertTypes

  • Type: Array or string (multiple types should be separated by commas)
  • Default: ['image/png']
  • Examples:
    • ['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.

convertSize

  • Type: number
  • Default: 5000000 (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:

convertSizeInput size (type)Output size (type)Compression ratio
5 MB1.87 MB (PNG)1.87 MB (PNG)0%
5 MB5.66 MB (PNG)450.24 KB (JPEG)92.23%
5 MB9.74 MB (PNG)883.89 KB (JPEG)91.14%

beforeDraw(context, canvas)

  • Type: Function
  • Default: null
  • Parameters:
    • context: 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%)';
  },
});

drew(context, canvas)

  • Type: Function
  • Default: null
  • Parameters:
    • context: 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);
  },
});

success(result)

  • Type: Function
  • Default: null
  • Parameters:
    • result: The compressed image (a File (read only) or Blob object).

The hook function to execute when successful to compress the image.

error(err)

  • Type: Function
  • Default: null
  • Parameters:
    • err: The compression error (an Error object).

The hook function executes when fails to compress the image.

⬆ back to top

Methods

abort()

Abort the compression process.

const compressor = new Compressor(file);

// Do something...
compressor.abort();

No conflict

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>

Browser support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Opera (latest)
  • Edge (latest)
  • Internet Explorer 10+

Contributing

Please read through our contributing guidelines.

Versioning

Maintained under the Semantic Versioning guidelines.

License

MIT Β© Chen Fengyuan

⬆ back to top