Vize

Image Cropper

Headless image cropper: aspect-locked crop box, zoom, rotation, keyboard nudging, canvas export.

Package @vizejs/ui/image-cropper
Maturity stable
Own the source vize lib pull image-cropper
Requires context, controllable-state, id, measure
Aliases image cropper, crop, avatar cropper, photo editor, image crop
Covers Cropper.js, react-easy-crop, react-image-crop, Ark UI Image Cropper

Usage

import { ImageCropperArea, ImageCropperGrid, ImageCropperHandle, ImageCropperImage, ImageCropper, ImageCropperRoot, ImageCropperViewport } from "@vizejs/ui/image-cropper";

Or copy the source into your project with vize lib pull image-cropper (see Source Distribution).

API

ImageCropperArea

Source: src/families/media/image-cropper/image-cropper-area.vue

Slots

Slot Slot props Description
default ImageCropperSlotState Handles, grid, and other overlays inside the crop box. Receives the cropper state.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

ImageCropperGrid

Source: src/families/media/image-cropper/image-cropper-grid.vue

Props

Prop Type Default Description
divisions number 3 Number of equal columns and rows; 3 draws the rule of thirds.

Slots

Slot Slot props Description
default ImageCropperSlotState Optional extra overlay content. Receives the cropper state.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

ImageCropperHandle

Source: src/families/media/image-cropper/image-cropper-handle.vue

Props

Prop Type Default Description
position (required) ImageCropperHandlePosition required Edge or corner this handle resizes, by compass direction.

Slots

Slot Slot props Description
default ImageCropperSlotState Optional handle content. Receives the cropper state.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

ImageCropperImage

Source: src/families/media/image-cropper/image-cropper-image.vue

Props

Prop Type Default Description
src (required) string required Image source. Relative, https:, blob:, and base64 data:image/* URLs are accepted; unsafe schemes are dropped. Use CORS-enabled URLs to export crops.
allowInsecure boolean false Permit unencrypted http: sources for local development.
alt (required) string required Native alternative text.
crossOrigin "" | "anonymous" | "use-credentials" undefined Native CORS mode; anonymous keeps canvas exports untainted for CORS images.
referrerPolicy ReferrerPolicy undefined Native referrer policy.

Events

Event Payload Description
load [nativeEvent: Event | null] Fired after the image loads and its natural size is known.
error [nativeEvent: Event] Fired when the image fails to load.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

ImageCropper

Source: src/families/media/image-cropper/image-cropper-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned base id. null and undefined select a deterministic fallback.
modelValue CropArea undefined Controlled crop in rotated-image pixels (v-model). undefined selects uncontrolled behavior.
defaultValue CropArea undefined Initial crop for uncontrolled use. undefined centers a crop covering autoCropArea.
zoom number undefined Controlled zoom factor (v-model:zoom); 1 fits the whole image.
defaultZoom number 1 Initial zoom for uncontrolled use.
rotation number undefined Controlled rotation in degrees (v-model:rotation).
defaultRotation number 0 Initial rotation for uncontrolled use.
aspectRatio number undefined Locked width / height ratio. undefined allows free resizing.
minWidth number undefined Minimum crop width in image pixels.
minHeight number undefined Minimum crop height in image pixels.
maxWidth number undefined Maximum crop width in image pixels.
maxHeight number undefined Maximum crop height in image pixels.
minZoom number 1 Smallest zoom factor.
maxZoom number 5 Largest zoom factor.
zoomStep number 0.1 Relative zoom change per key press or wheel notch.
rotationStep number 90 Degrees rotated by [ and ].
nudgeStep number 1 Image pixels moved per arrow key press (Shift multiplies by 10).
autoCropArea number 0.8 Fraction of the image the initial centered crop covers.
disabled boolean false Suppress every pointer and keyboard interaction.
messages Partial<ImageCropperMessages> undefined Localized accessible text. Missing keys fall back to English.

Events

Event Payload Description
update:modelValue [crop: CropArea] Fired when the crop requests a new controlled value.
update:zoom [zoom: number] Fired when zoom requests a new controlled value.
update:rotation [rotation: number] Fired when rotation requests a new controlled value.
change [crop: CropArea, reason: ImageCropperChangeReason] Fired after every distinct crop request, with its cause.
cropEnd [crop: CropArea] Fired when a pointer interaction or key press finishes changing the crop.

Slots

Slot Slot props Description
default ImageCropperSlotState Viewport, image, area, handles, and controls. Receives the cropper state.

Exposed

Member Type Description
crop —
disabled —
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.
interaction —
naturalSize —
ready —
reset —
rotation —
setCrop —
setRotation —
setZoom —
toBlob —
toDataUrl —
zoom —

ImageCropperRoot

Source: src/families/media/image-cropper/image-cropper-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned base id. null and undefined select a deterministic fallback.
modelValue CropArea undefined Controlled crop in rotated-image pixels (v-model). undefined selects uncontrolled behavior.
defaultValue CropArea undefined Initial crop for uncontrolled use. undefined centers a crop covering autoCropArea.
zoom number undefined Controlled zoom factor (v-model:zoom); 1 fits the whole image.
defaultZoom number 1 Initial zoom for uncontrolled use.
rotation number undefined Controlled rotation in degrees (v-model:rotation).
defaultRotation number 0 Initial rotation for uncontrolled use.
aspectRatio number undefined Locked width / height ratio. undefined allows free resizing.
minWidth number undefined Minimum crop width in image pixels.
minHeight number undefined Minimum crop height in image pixels.
maxWidth number undefined Maximum crop width in image pixels.
maxHeight number undefined Maximum crop height in image pixels.
minZoom number 1 Smallest zoom factor.
maxZoom number 5 Largest zoom factor.
zoomStep number 0.1 Relative zoom change per key press or wheel notch.
rotationStep number 90 Degrees rotated by [ and ].
nudgeStep number 1 Image pixels moved per arrow key press (Shift multiplies by 10).
autoCropArea number 0.8 Fraction of the image the initial centered crop covers.
disabled boolean false Suppress every pointer and keyboard interaction.
messages Partial<ImageCropperMessages> undefined Localized accessible text. Missing keys fall back to English.

Events

Event Payload Description
update:modelValue [crop: CropArea] Fired when the crop requests a new controlled value.
update:zoom [zoom: number] Fired when zoom requests a new controlled value.
update:rotation [rotation: number] Fired when rotation requests a new controlled value.
change [crop: CropArea, reason: ImageCropperChangeReason] Fired after every distinct crop request, with its cause.
cropEnd [crop: CropArea] Fired when a pointer interaction or key press finishes changing the crop.

Slots

Slot Slot props Description
default ImageCropperSlotState Viewport, image, area, handles, and controls. Receives the cropper state.

Exposed

Member Type Description
crop CropArea | null Current crop, or null until the image size is known.
zoom number Current zoom factor (1 fits the whole image).
rotation number Current rotation in degrees, normalized to [0, 360).
naturalSize CropSize | null Natural image size, or null until the image loads.
ready boolean Whether image and viewport are measured and geometry is published.
interaction ImageCropperInteraction Pointer or keyboard interaction in progress.
disabled boolean Whether every interaction is suppressed.
element HTMLDivElement | null Rendered root element.
setCrop (crop: CropArea) => boolean Request a crop (clamped to the constraints). Reports whether it changed.
setZoom (zoom: number) => boolean Request a zoom factor (clamped). Reports whether it changed.
setRotation (degrees: number) => boolean Request a rotation in degrees. Reports whether it changed.
reset () => void Restore the centered default crop, zoom 1, and rotation 0.
toBlob (options?: Omit<CropImageOptions, "rotation">) => Promise<Blob> Crop the loaded image into a Blob with the current crop and rotation (client-only).
toDataUrl (options?: Omit<CropImageOptions, "rotation">) => Promise<string> Crop the loaded image into a data URL with the current crop and rotation (client-only).

ImageCropperViewport

Source: src/families/media/image-cropper/image-cropper-viewport.vue

Props

Prop Type Default Description
wheelZoom boolean true Zoom around the pointer with the mouse wheel or trackpad.

Slots

Slot Slot props Description
default ImageCropperSlotState Image, crop area, and overlays. Receives the cropper state.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

Behavior

Normative state x input -> outcome table for image-cropper-root.vue, image-cropper-viewport.vue, image-cropper-image.vue, image-cropper-area.vue, image-cropper-handle.vue, and image-cropper-grid.vue (@vizejs/ui/image-cropper). Every row is proven by the named test.

Crops are { x, y, width, height } in the rotated image's bounding box, measured in natural image pixels, so cropImage and server-side croppers can reproduce the result. The viewport shows the bounds with contain scale times zoom; geometry is published only after the image and viewport are measured, so server and hydration markup agree.

ID State Input Outcome Evidence
IC1 unmeasured image load the centered autoCropArea crop is requested (init); image and area receive measured positions; the area is a labelled focusable group publishes the initial centered crop and measured geometry once the image loads
IC2 ready drag the area the crop moves by pointer delta / scale, stays in bounds, reports moving, and emits cropEnd on release; secondary buttons are ignored dragging the area moves the crop in image pixels and commits on release
IC3 ready drag a handle the opposite edge stays anchored; locked aspect ratios resize symmetrically around the crop center handles resize from their edge or corner, including aspect-locked crops
IC4 focused area arrows / Alt+arrows / + - / [ ] arrows nudge by nudgeStep (Shift ×10), Alt/Ctrl/Meta+arrows resize, +/- zoom around the crop center, [/] rotate by rotationStep keyboard nudges, resizes, zooms, and rotates the focused crop area
IC5 ready wheel / background drag the wheel zooms around the pointer; dragging outside the area pans the zoomed view within the image wheel zoom keeps the pointer anchor and background drags pan the zoomed view
IC6 ready rotation change bounds swap on right angles and the crop keeps its relative center, clamped into the new bounds (rotate) rotation swaps the bounds and re-fits the crop around its relative center
IC7 controlled crop / zoom / rotation requests requests are emitted while controlled values render; zoom clamps to minZoom..maxZoom; controlled crops are not re-initialized controlled crop, zoom, and rotation win until the parent accepts them
IC8 any reset() / messages / disabled reset restores zoom, rotation, and the default crop; messages localize the accessible text; disabled croppers ignore every input reset, localized messages, and disabled croppers
IC9 image unsafe / cached / replaced source unsafe sources are dropped, cached images are read on mount, and a different image size re-initializes the crop images are sanitized, read when already complete, and reset the crop when replaced
IC10 grid render divisions - 1 lines per axis expose offsets through --vize-ui-image-cropper-grid-offset the grid renders division lines with offsets
IC11 exposed instance toBlob() / toDataUrl() exports use the current crop and rotation through cropImage, and reject before an image loads exports the loaded crop through the canvas helper
IC12 missing provider setup compound parts fail closed with the shared context diagnostic compound parts require a matching root provider
IC13 geometry rotation rotation normalizes into [0, 360) and right angles swap bounds exactly normalizes rotation and computes rotated bounds exactly on right angles
IC14 geometry clamp crops clamp into bounds, size limits, and aspect ratios; bounds win over minimums clamps crops into bounds, size limits, and aspect ratios
IC15 geometry default crop default crops are centered by coverage and aspect ratio centers default crops by coverage and aspect ratio
IC16 geometry move moves stay inside the bounds moves crops by deltas without leaving the bounds
IC17 geometry free resize every handle resizes with an anchored opposite edge, never crossing and honoring limits resizes free crops from every handle with anchored opposite edges
IC18 geometry locked resize corners follow the dominant axis; edges grow symmetrically; bounds and limits cap the size resizes aspect-locked crops along the dominant axis and within bounds
IC19 geometry re-fit re-fitting keeps the relative center and falls back to a centered crop from empty bounds re-fits crops after bounds change by keeping the relative center
IC20 geometry view mapping contain scale, clamped view centers, viewport mapping, and zoom anchors are exact maps the view with contain scale, clamped centers, and zoom anchors
IC21 canvas helper crop the image is rotated into its bounds, cut, downscaled to maxWidth/maxHeight, and encoded as a Blob or data URL crops, rotates, downscales, and encodes blobs and data URLs
IC22 canvas helper URL source URL sources load with the requested CORS mode before cropping; load errors reject loads URL sources with CORS before cropping
IC23 canvas helper failure empty crops, missing canvases, load failures, and encode failures reject with typed ImageCropperError codes rejects empty crops, missing canvases, failed loads, and failed encodes with typed errors
IC24 canvas helper pending image pending image elements are awaited waits for pending images to load
IC25 SSR isolated requests markup is byte-identical and carries no measured geometry renders byte-identical cropper markup without measured geometry on the server
IC26 SSR / hydration hydrate server markup hydrates without warnings or node replacement hydrates cropper markup without warnings or node replacement
IC27 types compile crops, handles, reasons, interactions, export overloads, and exposes are closed and read-only image-cropper.types.test-d.ts

Pinch-to-zoom gestures are not interpreted; touch users zoom with the consumer's controls (setZoom) or the keyboard.