Skip to content

Repository files navigation

react-image-optimizer

Resize, convert, and compress images in the browser until they satisfy strict upload requirements — the kind government portals, visa/passport forms, and job application sites love to enforce:

"JPEG only, under 20KB, exactly 200×200 pixels."

No server round-trip. No general-purpose "make images smaller" library — give it a target size and/or dimensions, and it runs a full pipeline (EXIF correction, crop, resize, format conversion, and a binary search over encode quality) until the output actually fits, then hands you back a File ready to append to FormData.

packages/
  core/    @poly67/core   — the optimization engine (zero deps)
  react/   @poly67/react  — <ImageOptimizer /> + useImageOptimizer
examples/
  basic-app/                             — Vite demo of all three usage patterns

Install

npm install @poly67/react   # includes core as a dependency
# or, framework-agnostic:
npm install @poly67/core

Quick start

import { ImageOptimizer } from "@poly67/react";

<ImageOptimizer
  maxSizeKB={20}
  maxWidth={200}
  maxHeight={200}
  format="jpeg"
  quality={0.9}
  autoOptimize
  preview
  onProgress={(p) => console.log(p.stage, p.progress)}
  onSuccess={(result) => {
    const formData = new FormData();
    formData.append("photo", result.file);
    fetch("/api/upload", { method: "POST", body: formData });
  }}
  onError={(error) => console.error(error)}
/>;

Or without a component:

import { optimizeImage } from "@poly67/core";

const { file } = await optimizeImage(input.files[0], {
  maxSizeKB: 20,
  maxWidth: 200,
  maxHeight: 200,
  format: "jpeg",
});

Or with the hook, for a fully custom UI:

import { useImageOptimizer } from "@poly67/react";

const { optimize, isOptimizing, progress, result, error } = useImageOptimizer({
  maxSizeKB: 20,
  maxWidth: 200,
  maxHeight: 200,
});

See packages/core/README.md and packages/react/README.md for full API references, and examples/basic-app for a runnable demo of all three patterns (government ID photo, job portal resume photo, direct API call).

Features

  • Resize while preserving aspect ratio (or stretch to an exact box)
  • contain (fit inside, letterbox) or cover (fill and crop) modes
  • Convert between JPEG / PNG / WebP
  • Binary-search encode quality to hit a target file size accurately
  • Progressive downscale fallback for lossless formats or unreachable targets
  • Optional pixel-rect crop
  • White (or custom) background flattening when converting transparent PNG → JPEG
  • EXIF orientation correction (hand-parsed, no dependency)
  • AbortController support and stage-by-stage progress callbacks
  • Async, Promise-based API; returns both File and Blob
  • Drag-and-drop compatible out of the box
  • 100% client-side — no server, no upload, no network call

How it works

  1. Read the file and decode it (createImageBitmap, falling back to HTMLImageElement).
  2. Correct EXIF orientation — phones often store landscape pixel data with a rotation flag rather than physically rotating it; this is parsed by hand from the JPEG's APP1/EXIF segment.
  3. Crop, if a crop rect was given.
  4. Resize into maxWidth/maxHeight, per fit and preserveAspectRatio.
  5. Encode, filling a background first if the source had transparency and the target format doesn't support alpha (or a background was set explicitly).
  6. If maxSizeKB is set: binary-search quality between minQuality and quality for the highest quality that still fits the byte budget.
  7. If quality alone can't reach the target (lossless PNG, or a very aggressive size target): progressively downscale the canvas and repeat step 6, up to a safety limit.
  8. Return an OptimizeResult with the final File, Blob, dimensions, size, quality, and how many encode attempts it took.

Development

This is an npm-workspaces monorepo.

npm install          # installs and links all workspace packages
npm run build         # builds packages/core then packages/react
npm run test          # runs the full Vitest suite (both packages)
npm run lint           # ESLint across the repo
npm run typecheck      # tsc --noEmit in every workspace
npm run dev             # runs the example app (examples/basic-app)

Releases are managed with Changesets:

npx changeset            # describe your change
# on merge to main, CI opens a "Version Packages" PR;
# merging that PR publishes to npm.

Browser support

Requires Blob and Canvas (or OffscreenCanvas). createImageBitmap is used when available for faster, non-blocking decode, with an automatic HTMLImageElement + <canvas> fallback for older engines.

Contributing

Issues and PRs welcome. Please run npm run lint && npm run typecheck && npm run test before opening a PR, and add a changeset (npx changeset) describing your change.

License

MIT © Keerthan D

About

No description, website, or topics provided.

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages