Root
Root loads the photo, keeps the crop and holds every other part.
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.
- src string | Blob | null
- The photo: a URL or a
Blobsuch as aFile.nullclears it,undefinedleaves 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 newpreviewEdge,crossOriginorsvgloads the photo again and firesonLoadagain. The crop stays, so keeppreviewEdgesteady - svg boolean = true
falserefuses SVG files, which then fail withdecode. Withtrue, an SVG that would freeze the page fails withtoo-large, see security- image Size | null
- The photo's pixel size up front, read once
- value CropValue
- Controls the whole value.
onValueChangedoes 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. ARootthat started as a circle takesdefaultValueor"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,pluginsandfocalLengththen come fromuseCropper - actionsRef Ref<Cropper>
- Gives you the cropper, for calls like
rotate90()from a button - plugins readonly CropperPlugin[]
- Runs each plugin's
initonce, whenRootmakes the cropper, see cropper - disabled boolean = false
- Ignores gestures, keys, buttons and the cropper methods that move the crop.
valueandsetValue()still apply. Setsdata-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,ZoomandAspectGroup. In RTL their left and right swap.Area's arrow keys stay physical. With no value, each of these parts reads its computeddirectionat the press. With a value,Rootalso sets the HTMLdirattribute - 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
Rootmakes the cropper.cropper.setOptions()changes it, from the nextsetValue(),reset()orsetImage()
| Attribute | When |
|---|---|
| data-interacting | During a gesture and for a short hold after it, see accessibility |
| data-panning | From a press on the photo until the pointer lets go, even if it never moves |
| data-pinching | While two fingers pinch or twist on a touch screen, or a trackpad pinches in Safari |
| data-resizing | From a press on a handle until the pointer lets go, even if it never moves |
| data-rotating | From a press on the dial until the pointer lets go, even if it never moves, and while the dial glides after |
| data-zooming | While the wheel pans or zooms, from a press on Zoom until the pointer lets go, or while a useZoom slider moves |
| data-settling | While any animation runs: a glide, a spring back, a recentre or a button's move |
| data-changed | While Reset has a change to undo |
| data-mirrored | While the photo is flipped |
| data-overscroll | While you pull past an edge or a zoom limit |
| data-disabled | While disabled is set |
| data-reduced-motion | While 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" |