Skip to content

Latest commit

 

History

History
136 lines (106 loc) · 4.06 KB

File metadata and controls

136 lines (106 loc) · 4.06 KB

@poly67/react

React bindings for @poly67/core: a drag-and-drop <ImageOptimizer> component and a useImageOptimizer hook that shrink uploaded images to fit strict size/dimension requirements — entirely in the browser, no server round-trip.

See the root README for the full guide and the algorithm explanation.

Install

npm install @poly67/react

react and react-dom (18 or 19) are peer dependencies.

<ImageOptimizer />

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

function ProfilePhotoUpload() {
  return (
    <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)}
    />
  );
}

Ships with a minimal, accessible default UI (dropzone + native file input + preview + status text), which you can fully replace with a render-prop:

<ImageOptimizer maxSizeKB={20} maxWidth={200} maxHeight={200}>
  {({ isDragActive, isOptimizing, previewUrl, result, error, openFileDialog }) => (
    <div className={isDragActive ? "dropzone--active" : "dropzone"}>
      {previewUrl && <img src={previewUrl} alt="" />}
      <button type="button" onClick={openFileDialog} disabled={isOptimizing}>
        {isOptimizing ? "Optimizing…" : "Choose photo"}
      </button>
      {result && <p>{result.sizeKB.toFixed(1)}KB, ready to upload</p>}
      {error && <p role="alert">Couldn't process that image.</p>}
    </div>
  )}
</ImageOptimizer>

Props

All OptimizeOptions fields (maxSizeKB, maxWidth, maxHeight, format, quality, minQuality, background, preserveAspectRatio, fit, crop, correctOrientation) are accepted directly, plus:

Prop Type Default Description
autoOptimize boolean true Optimize as soon as a file is selected/dropped.
preview boolean true Show an object-URL preview of the selected file.
multiple boolean false Allow selecting more than one file.
accept string "image/*" Forwarded to the file input.
disabled boolean false Disable interaction.
className / style Applied to the root element.
children ReactNode | (state) => ReactNode Custom UI / render prop.
onProgress (p: OptimizeProgress) => void Pipeline progress updates.
onSuccess (r: OptimizeResult) => void Called once optimization finishes.
onError (e: unknown) => void Called if optimization throws.

Ref API

When autoOptimize is false, trigger optimization imperatively:

const ref = useRef<ImageOptimizerHandle>(null);

<ImageOptimizer ref={ref} autoOptimize={false} maxSizeKB={20} />
<button onClick={() => ref.current?.optimize()}>Optimize</button>

ImageOptimizerHandle: { optimize(), reset(), openFileDialog() }.

useImageOptimizer(options?)

For fully custom UIs:

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

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

  return (
    <input
      type="file"
      accept="image/*"
      onChange={(e) => e.target.files?.[0] && optimize(e.target.files[0])}
    />
  );
}

Returns { optimize, isOptimizing, progress, result, error, reset, abort }. Calling optimize() again automatically aborts a still-running previous call.

Direct core usage

optimizeImage is re-exported for one-off calls that don't need component state:

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

const { file } = await optimizeImage(rawFile, { maxSizeKB: 20 });

License

MIT