jpegr
v2.3.0
Published
🖼️ Convert images to JPEG with size-targeted compression directly from browser with high compatibility rate.
Maintainers
Readme
🖼️ JPEGR
A browser module to take all image formats supported by HTMLCanvasElement and convert them to JPEG with size-targeted compression to meet a configurable maximum output size.
Key features
- Auto preview and easy upload to backend endpoints.
- Comprehensive fallback support for legacy and lite browsers.
- Automatic compression starting from a chosen initial quality with granular steps.
- Allows using the original image when both format and size are within expectations.
- Normalizes EXIF orientation for JPEG inputs (common "bug" in smartphone photos).
Installation
npm i jpegrQuick start
import { JPGER } from 'jpegr';
const preview = document.querySelector<HTMLImageElement>('#preview');
const input = document.querySelector<HTMLInputElement>('#file');
const button = document.querySelector<HTMLButtonElement>('#upload');
const jpegr = new JPGER({ preview });
input?.addEventListener('change', () => {
jpegr.process(input); // It will automatically show preview ✨
});
button?.addEventListener('click', () => {
if (!jpegr.status.hasImage || jpegr.status.error) {
alert('Upload a valid image to proceed.');
return;
}
jpegr.upload('/api/upload'); // That's it ⭐️
});- See the Examples section for React and real-world usage approaches and the Overview section for visual example.
- See the ./examples directory for complete and functional working examples.
Usage
Constructor
new JPGER(options?: JPGEROptions)
Creates a new processor instance with its own internal cache and configuration defaults.
import { JPGER } from 'jpegr';
const jpegr = new JPGER({
preview: null, // Specify an image element to preview the processed image.
maxSize: 1 * 1024 * 1024,
minQuality: 0.1,
maxQuality: 1,
compressionStep: 0.1,
forceCompression: false,
backgroundColor: '#000000',
});- The example above uses all the default options.
Methods
process
Processes an HTMLInputElement (<input type="file">) or an image File | Blob.
const result = await jpegr.process(inputElement);upload
Uploads the cached processed image to a backend URL as multipart form data.
const response = await jpegr.upload('/api/upload');Options:
field(default:"image")name(default:"image.jpeg")init(optionalRequestInitmerged into the fetch options)
const response = await jpegr.upload('/api/upload', {
field: 'image',
name: 'image.jpeg',
init: {
headers: {
// ...
},
},
});clear
Clears the stored processed image from memory.
jpegr.clear();Examples
Vanilla JS 🍦
<input id="file" type="file" accept="image/*" />
<img id="preview" width="100%" />
<script type="module" src="./main.mjs"></script>import { JPGER } from 'https://cdn.jsdelivr.net/npm/jpegr@latest/lib/index.mjs';
const input = document.querySelector('#file');
const preview = document.querySelector('#preview');
const jpegr = new JPGER({ preview });
input.addEventListener('change', async () => {
const result = await jpegr.process(input);
if (!result.success) {
console.error(result.error);
jpegr.clear();
}
});[!TIP]
- You can use both multiple
HTMLInputElement,File, andBlobin theprocessmethod.
Merging multiple images
import { JPGER } from 'https://cdn.jsdelivr.net/npm/jpegr@latest/lib/index.mjs';
const input = document.querySelector('#file');
const preview = document.querySelector('#preview');
const jpegr = new JPGER({ preview });
input.addEventListener('change', async () => {
const files = Array.from(input.files);
const result = await jpegr.merge(files, 'vertical');
if (!result.success) {
console.error(result.error);
jpegr.clear();
}
});[!TIP]
- You can use both multiple
HTMLInputElement,File, andBlobin themergemethod.- See the ImageMescler.tsx file for a complete React example of merging multiple images.
React ⚛️
import type { ProcessResult } from 'jpegr';
import type { ChangeEvent } from 'react';
import { JPGER } from 'jpegr';
import { useRef, useState } from 'react';
export default () => {
const { current: jpegr } = useRef(new JPGER());
const [result, setResult] = useState<ProcessResult | null>(null);
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
jpegr.process(event.currentTarget).then((data) => {
if (!data.success) jpegr.clear();
setResult(data);
});
};
return (
<>
<input type='file' accept='image/*' onChange={onChange} />
{result?.image && (
<img src={result.image.src} alt='Selected image preview' />
)}
</>
);
};Overview
- Check this example in ./examples/vite.
Troubleshooting
Browser support
JPEGR uses the following browser APIs:
HTMLCanvasElementBlob→ falls back toFileUint8Array→ falls back toDataViewcreateImageBitmapandURL.createObjectURL→ falls back toFileReader→ falls back toResponseAPI
[!TIP]
If critical features are unavailable, you can use the static method
JPGER.canProcess()to check for JPEGR support before attempting image processing, for example:import { JPGER } from 'jpegr'; if (!JPGER.canProcess()) { // ... }
Debugging
Check feature availability in browsers:
import { JPGER } from 'jpegr';
const support = JPGER.getRuntimeSupport();
/**
* {
* FileReader: true,
* Blob: true,
* File: true,
* HTMLCanvasElement: true,
* canvasToBlob: true,
* createObjectURL: true,
* createImageBitmap: true,
* Response: true,
* Uint8Array: true,
* DataView: true
* }
*/Security notes
- This module runs entirely on the client side and does not upload anything unless you call
upload(). - Treat user-provided images as untrusted input. While browser image decoders are hardened, you should still validate server-side uploads.
License
JPEGR is under the MIT License. Copyright © 2025-present Weslley Araújo and JPEGR contributors.
