Skip to navigation

Root

Root loads the photo, keeps the crop and holds every other part.

Open
Download

Anatomy

Everything goes inside Root. Image, Mask and Area go in Viewport. The controls can sit anywhere, in any wrapper:

<Cropper.Root src={photo}>
  <Cropper.Viewport style={{ height: 400 }}>
    <Cropper.Image />
    <Cropper.Mask />
    <Cropper.Area aria-label="Crop area">
      <Cropper.Grid />
    </Cropper.Area>
  </Cropper.Viewport>
  <Cropper.Dial aria-label="Straighten">
    <Cropper.DialTicks />
  </Cropper.Dial>
  <div className="toolbar">
    <Cropper.Rotate>Rotate</Cropper.Rotate>
    <Cropper.Flip>Flip</Cropper.Flip>
    <Cropper.Reset>Reset</Cropper.Reset>
  </div>
</Cropper.Root>

Lock the cropper while you save

disabled ignores every gesture, key and button press, and every cropper method that moves the crop, such as rotate90() or setZoom(), except setValue(), setImage() and setOptions(). Root gets data-disabled, so CSS can grey it out. value and a new src still apply, and aspect, rotation and zoom set while disabled apply once it ends:

<Cropper.Root src={photo} disabled={saving}>
  <Cropper.Viewport style={{ height: 400 }}>
    <Cropper.Image />
    <Cropper.Mask />
    <Cropper.Area aria-label="Crop area" />
  </Cropper.Viewport>
</Cropper.Root>

Grey out the buttons while it is set:

[data-zriz-item="root"][data-disabled] button {
  opacity: 0.4;
  pointer-events: none;
}

The demo above sets disabled while its download runs. It adds handles, a grid and the styles from the zriz site.

Dim the controls during a gesture

Root carries data attributes for what the cropper is doing. This rule fades the toolbar during any gesture, a key press included, and for a short hold after it, see accessibility:

[data-zriz-item="root"][data-interacting] .toolbar {
  opacity: 0.4;
}

Put it in a drawer

Root carries data-vaul-no-drag and data-base-ui-swipe-ignore, so a swipe on the photo or a control moves the crop and leaves a Vaul or Base UI drawer where it is. Give the drawer a handle or free space outside Root. To let the drawer drag from inside, set the attribute to null on Root, such as data-vaul-no-drag={null}.

API

Root

Renders a <div>. Every other part goes inside it. Every part also takes className, render and the props of its element, see composition. className and render get { snapshot, chrome }. snapshot holds the fields useCropperState lists on hooks. chrome holds grid, active, transition and overscroll, the values behind data-grid, data-interacting, data-transition and data-overscroll.

Prop
src string | Blob | null
The photo: a URL or a Blob such as a File. null clears it, undefined leaves it alone
crossOrigin "anonymous" | "use-credentials"
The CORS mode when the photo is on another domain
previewEdge number
The longest side of the preview in pixels. The default follows the screen, see loading images. A grid of small Roots saves memory with a low one. A new previewEdge, crossOrigin or svg loads the photo again and fires onLoad again. The crop stays, so keep previewEdge steady
svg boolean = true
false refuses SVG files, which then fail with decode. With true, an SVG that would freeze the page fails with too-large, see security
image Size | null
The photo's pixel size up front, read once
value CropValue
Controls the whole value. onValueChange does not fire when you set it
defaultValue Partial<CropValue>
The starting value. Fields you leave out use the defaults
aspect "free" | "original" | number
Controls the aspect alone, a ratio like 4 / 3
rotation number
Controls the straighten angle alone, up to 45° each way
zoom number
Controls the zoom alone. 1 is the largest crop
shape "rect" | "circle" = "rect"
The crop shape. A circle locks the aspect to 1, draws a round mask and exports a round PNG, and the stylesheet hides the corner handles, see avatar. Back to "rect", the crop takes the aspect it held before. A Root that started as a circle takes defaultValue or "free", and reset follows
minSize Size = { width: 1, height: 1 }
The smallest crop in source pixels, along the photo's own sides. A large one narrows the straighten range. Exporting says when a crop can come out smaller
maxZoom number = 15
The deepest zoom. Every gesture, key and control stops there
onValueChange (value, details) => void
At most once per frame while the crop moves, with a reason and cancel(), see events
onValueCommit (value, details) => void
Called once when a gesture or a write is done, with pixelCrop, the export plan, see events
onLoad (image) => void
Called when the photo is decoded and ready to crop
onError (error) => void
Called with a typed error when the photo cannot load, see errors
cropper Cropper
Uses an instance from useCropper. image, defaultValue, plugins and focalLength then come from useCropper
actionsRef Ref<Cropper>
Gives you the cropper, for calls like rotate90() from a button
plugins readonly CropperPlugin[]
Runs each plugin's init once, when Root makes the cropper, see cropper
disabled boolean = false
Ignores gestures, keys, buttons and the cropper methods that move the crop. value and setValue() still apply. Sets data-disabled
motion "auto" | "always" | "reduce" = "auto"
Follows the system motion setting, or forces full or short motion
recenterDelay number = 300
Milliseconds after a gesture before the crop recentres and the scrim comes back
dir "ltr" | "rtl"
The reading direction for Dial, Zoom and AspectGroup. In RTL their left and right swap. Area's arrow keys stay physical. With no value, each of these parts reads its computed direction at the press. With a value, Root also sets the HTML dir attribute
locale string
Formats the values screen readers hear
focalLength number = 26
The lens as a 35 mm equivalent, used for perspective correction. At a strong tilt, zriz lengthens a lens too short to keep the photo in front of it. Read once, when Root makes the cropper. cropper.setOptions() changes it, from the next setValue(), reset() or setImage()
AttributeWhen
data-interactingDuring a gesture and for a short hold after it, see accessibility
data-panningFrom a press on the photo until the pointer lets go, even if it never moves
data-pinchingWhile two fingers pinch or twist on a touch screen, or a trackpad pinches in Safari
data-resizingFrom a press on a handle until the pointer lets go, even if it never moves
data-rotatingFrom a press on the dial until the pointer lets go, even if it never moves, and while the dial glides after
data-zoomingWhile the wheel pans or zooms, from a press on Zoom until the pointer lets go, or while a useZoom slider moves
data-settlingWhile any animation runs: a glide, a spring back, a recentre or a button's move
data-changedWhile Reset has a change to undo
data-mirroredWhile the photo is flipped
data-overscrollWhile you pull past an edge or a zoom limit
data-disabledWhile disabled is set
data-reduced-motionWhile short motion is on
data-orientation"landscape" above 1, "portrait" below 1, "square" at 1 and while the aspect is "free" or "original"
data-grid"coarse", "fine" or "none", see crop area
data-transition"rotate", "flip", "morph" or "recenter" while that transition runs. "morph" covers reset, an aspect pick, an orientation swap and undo. A reset or undo across a quarter turn runs as "rotate", with the crop frame and the photo outside it still in view
data-shape"rect" or "circle"