Built on Vercel’s UI SDK 0.1.0-beta.1. Its API may change.
Image cropper
An image cropper on Vercel's UI SDK with a resizable selection, aspect ratios, a round avatar frame, zoom, rotate and flip.
- Component
- 10 parts
- 6 examples
@tabler/icons-react@ui-sdk/core@ui-sdk/reactclass-variance-authority
pnpm dlx shadcn@latest add https://hextaui.com/r/ui-sdk-image-cropper.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @ui-sdk/react@0.1.0-beta.1 @ui-sdk/core@0.1.0-beta.1 @tabler/icons-react class-variance-authority cnCopy and paste the following code into your project.
components/ui/ui-sdk-image-cropper.tsx components/ui/button.tsx Update the import paths to match your project setup.
UI SDK adds a little CSS of its own in a cascade layer named ui-sdk. Declare that layer at the top of your global stylesheet, above the Tailwind import, so your classes win over it.
The crop is a box in the image’s own pixels, with its rotation and flip. Pass it to cropImage to get a file you can upload. An image from another site needs CORS, or the browser won’t let the page read its pixels.
With mode="frame", the selection stays still and you pan and zoom the image under it. shape="circle" rounds it, so pass shape: "circle" to cropImage as well.
ImageCropperRotate turns the image a quarter clockwise and ImageCropperFlip mirrors it. Without children they’re icon buttons with English labels.
Crop a photo from a file input with an object URL. Give the cropper a key so a new photo starts a fresh crop.
onCropCommit runs once a drag or key press ends, so save there. A controller from createImageCropperController() sets the crop from anywhere.
A disabled cropper can’t be focused or changed, and its zoom and buttons are disabled.
| Key | Action |
|---|---|
| ←→↑↓ | Moves the selection, or on a handle, moves its edges. |
| Shift← | Moves ten times as far. |
| +- | Zooms in or out about the crop’s middle. |
| R | Turns the image a quarter clockwise, or back with Shift. |
| Esc | During a drag, puts the crop back where the drag started. |
The cropper is a group named by aria-label. The selection and each handle are sliders that say the crop in words, like “300 by 300 pixels, at 50, 0”, and the zoom says its level, like “125%”. Those names are in English, so pass aria-label to each part to name it in your page’s language.
The grid only fades in while you drag, and not at all when motion is reduced. Handles get a larger hit area on touch screens, the page doesn’t scroll during a drag, and in forced colors mode the selection gets a system-colored border.
Each part wraps the UI SDK part of the same name and accepts all of its props, including render and a className that can be a function of the part’s state.
| Prop | Type | Default |
|---|---|---|
src | string | – |
cropThe crop in image pixels, with rotation, flipX and flipY. | ImageCrop | – |
defaultCrop | ImageCrop | – |
onCropChange | (crop, details) => void | – |
onCropCommitRuns once a drag, key press or wheel ends. | (crop, details) => void | – |
mode | "box" | "frame" | "box" |
aspectRatio | number | – |
shape | "rectangle" | "circle" | "rectangle" |
minWidth | number | – |
minHeight | number | – |
maxZoom | number | 10 |
disabled | boolean | false |
controllerFrom createImageCropperController(). | ImageCropperController | – |
| Attribute | Description |
|---|---|
data-slot="image-cropper" | Target the cropper in CSS. |
data-mode | box or frame. |
data-shape | rectangle or circle. |
data-loading | Present until the image loads. |
data-error | Present when the image failed to load. |
data-dragging | Present while a pointer drags. |
| Prop | Type | Default |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | – |
| Attribute | Description |
|---|---|
data-slot="image-cropper-viewport" | Target the viewport in CSS. |
--image-cropper-aspect-ratio | The image’s width over its height once it loads. The viewport takes it as its aspect ratio. |
| Prop | Type | Default |
|---|---|---|
altRequired. | string | – |
render | ReactElement | (props, state) => ReactElement | – |
| Prop | Type | Default |
|---|---|---|
aria-label | string | "Crop" |
render | ReactElement | (props, state) => ReactElement | – |
| Attribute | Description |
|---|---|
data-slot="image-cropper-selection" | Target the selection in CSS. |
| Prop | Type | Default |
|---|---|---|
childrenDefaults to lines in thirds. | ReactNode | – |
| Prop | Type | Default |
|---|---|---|
positionThe edge or corner it resizes. | "n" | "e" | "s" | "w" | "ne" | "nw" | "se" | "sw" | – |
ImageCropperHandles renders all eight. Handles don’t show in frame mode.
A native range input from 1, the largest crop, to maxZoom.
| Prop | Type | Default |
|---|---|---|
childrenDefaults to an icon with the label Rotate. With children it’s a small text button. | ReactNode | – |
| Prop | Type | Default |
|---|---|---|
direction | "horizontal" | "vertical" | "horizontal" |
childrenDefaults to an icon with an English label. | ReactNode | – |