ComponentsEditors & drag-and-drop

Image cropper

Crops an image to a ratio with drag, pinch, wheel and keyboard, plus a toolbar: ratio presets, zoom, rotate ±90°, flip and reset. It outputs a non-destructive crop rect in source pixels for asset.crops; getCroppedBlob() bakes a copy when you need a new file.

react-easy-crop media/image-cropper.tsxtheme · material · accent · density · font

Drag or use the arrow keys to move the image. Scroll, pinch or use the slider to zoom.

Move or zoom the image to see the crop rect

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { ImageCropper } from "@vamos-ui/react/media";

<ImageCropper src={asset} defaultAspect={1.91}
  onChange={(rect, preset) => adapter.update(asset.id, { crops: { ...asset.crops, [preset?.name ?? "custom"]: rect } })} />

Keyboard: focus the crop area and use the arrow keys to move; the zoom slider takes arrows, Page Up/Down, Home/End.

Free-aspect drag handles are not supported (presets cover most needs).

CropRect coordinates are in the rotated and flipped source space, matching getCroppedBlob.

Examples

Round avatar (HR profile)

shape="round" locks 1:1; transform={false} hides rotate and flip.

Drag or use the arrow keys to move the image. Scroll, pinch or use the slider to zoom.

Named presets

Each preset can carry a name; onChange reports it so you store asset.crops[name].

Drag or use the arrow keys to move the image. Scroll, pinch or use the slider to zoom.

Last saved: —

Save as copy

getCroppedBlob() renders the crop on a canvas (rotation- and flip-aware, optional maxWidth) for adapter.upload.

Drag or use the arrow keys to move the image. Scroll, pinch or use the slider to zoom.

API

ImageCropper

PropTypeDefault
src
Image to crop. Must allow CORS for getCroppedBlob.
string | MediaAsset—
value / defaultValue
Initial crop (e.g. asset.crops.og) in source pixels.
CropRect | null—
onChange
Fires when a drag, zoom or key press ends. rect = { x, y, width, height, rotation?, flip? }.
(rect: CropRect, preset?: CropPreset) => void—
aspect / defaultAspect / onAspectChange
Controlled or uncontrolled ratio.
number | "original"first preset
presets
Ratio buttons; labels get the ratio in the locale's decimals.
{ label; value: number | "original"; name? }[]Original, Square, Social 1.91:1, Landscape 16:9, Classic 4:3, Portrait 4:5
shape
round = avatar mask, 1:1.
"rect" | "round""rect"
grid
Rule-of-thirds grid.
booleantrue
minZoom / maxZoom
Zoom range.
number1 / 4
toolbar
Show the toolbar.
booleantrue
transform
Allow rotate and flip.
booleantrue
height
Stage height.
number | string"22rem"
labels
cropper, zoom, rotateLeft, flipH, reset, preset names… (dictionary namespace imageCropper).
Partial<ImageCropperLabels>—

getCroppedBlob(src, rect, options?)

PropTypeDefault
type
Output format.
"image/webp" | "image/jpeg" | "image/png""image/webp"
quality
0..1.
number0.9
maxWidth
Downscale wider crops.
number—

formatAspectRatio(value, locale?)

PropTypeDefault
→
16/9 → "16:9", 1.91 → "1.91:1" (tr: "1,91:1").
string—