Settings
Every option, its valid values, and its default.
Any option with a fixed set of valid string values
(
frame.shape, mouseWheelZoom,
zoomerPosition, and export()/upload()'s
type/format) is validated at runtime,
not just via TypeScript types. A typo like zoomerPosition: "bttom"
logs a console.warn naming the bad value and the valid options,
then falls back to the default — it never silently misbehaves.Constructor options
new Kiri(container, {
frame: {
shape: "circle", // "rectangle" | "circle" | "rounded-rectangle"
width: 200,
height: 200,
cornerRadius: 20, // only used when shape is "rounded-rectangle"
},
minZoom: 1,
maxZoom: 4,
rotatable: true,
flippable: true,
resizableFrame: false,
lockAspectRatio: false, // only matters when resizableFrame is true
movableFrame: false, // invert interaction: freeze the image, move the frame instead
mouseWheelZoom: true, // or "ctrl" to require Ctrl+wheel
useExifOrientation: true,
autoSizeStage: true,
showZoomer: false,
zoomerPosition: "bottom", // "top" | "bottom" | "left" | "right"
filters: { brightness: 1, contrast: 1, saturation: 1, sharpness: 1, grayscale: false, sepia: false },
uploader: undefined,
});
| Option | Type | Valid values | Default | Description |
|---|---|---|---|---|
frame.shape | string | "rectangle", "circle", "rounded-rectangle" | "rectangle" |
The shape of the crop selection. "circle" and
"rounded-rectangle" are real clips on export too
(transparent corners on PNG/WebP), not just a visual overlay — see
Architecture. |
frame.width | number | any positive pixel size | 200 |
Width of the crop selection, in pixels. This is also the default
output width for export(). |
frame.height | number | any positive pixel size | 200 |
Height of the crop selection, in pixels. Also the default output
height for export(). |
frame.cornerRadius | number | any non-negative pixel size | 20 |
Only matters when frame.shape is
"rounded-rectangle". The corner radius, in pixels, of both
the on-screen frame and the export clip. If the output size passed to
export() differs from frame.width/height,
the radius scales proportionally so it still looks the same. |
minZoom | number | any positive number ≤ maxZoom | 1 |
The lower bound on zoom — how far the user (or setZoom())
can zoom out. 1 means "the image is only ever as
small as needed to fully cover the frame" (its natural minimum); you
can't go smaller than that regardless of this setting. Raise it (e.g.
1.5) to force the image to always fill more of the frame
than its bare minimum. |
maxZoom | number | any positive number ≥ minZoom | 4 |
The upper bound on zoom — how far the user (or setZoom())
can zoom in. Lower it to stop people zooming in so far the
exported image looks pixelated. |
rotatable | boolean | true, false | true |
When true, calling cropper.rotate(90)
actually rotates the image 90°, and you can call it repeatedly to keep
rotating. When false, rotate() becomes a
silent no-op — nothing happens, no error — which is useful if you want
to hide/disable a rotate button in your own UI and guarantee rotation
can't happen even if that call is somehow still made. It has no effect
on drag/zoom/flip — those keep working either way. |
flippable | boolean | true, false | true |
Same idea as rotatable, but for
flipHorizontal()/flipVertical(). When
false, both become no-ops. |
resizableFrame | boolean | true, false | false |
When true, a small drag handle appears at each of the
frame's four corners — dragging one calls setFrameSize()
for you, live, letting the end user resize the crop selection with the
mouse/touch instead of only via code. See the
resizable frame example. |
lockAspectRatio | boolean | true, false | false |
Only matters when resizableFrame is also true.
When true, dragging a corner handle preserves the frame's
aspect ratio (whatever it was when the drag started) instead of
letting width and height resize independently — useful for a fixed
shape like a 1:1 avatar or a 16:9 thumbnail that the end user can
still resize. |
movableFrame | boolean | true, false | false |
Inverts the interaction model. When true, the image is
displayed at a fixed size and never pans or zooms —
setZoom()/setOffset() become no-ops, and mouse
wheel/pinch zoom stop doing anything. Dragging (or arrow keys) moves the
frame over the static image instead, via
setFramePosition(). Combine with resizableFrame
to also resize the frame in place — it's capped at the image's own
bounds instead of growing the image to compensate, since there's no
auto-zoom left to absorb the difference. rotate()/flip
still work, transforming the whole static image. |
mouseWheelZoom | boolean | string | true, false, "ctrl" | true |
Whether scrolling the mouse wheel (or a trackpad pinch) over the
stage zooms the image. false disables it entirely (zoom
only via setZoom() or a showZoomer slider).
"ctrl" requires holding the Ctrl key while scrolling to
zoom — useful on a page where you also want normal page-scroll to work
when the cursor happens to be over the cropper. |
useExifOrientation | boolean | true, false | true |
When true, load() reads EXIF orientation
data from a File/Blob (e.g. a photo straight
off a phone camera) and automatically applies the correct
rotation/flip so it displays right-side up. Set false to
skip that check (e.g. if you've already normalized images
server-side). |
autoSizeStage | boolean | true, false | true |
When true, the stage (the visible box) sizes itself to
the frame's dimensions plus a small margin — you don't need any CSS on
the container element for it to look right. Set false if
you'd rather the stage fill its container via your own CSS instead
(100% width/height) — see Architecture. |
showZoomer | boolean | true, false | false |
When true, Kiri renders its own
<input type="range"> zoom slider next to the stage —
no manual wiring needed. See the
zoom slider example. |
zoomerPosition | string | "top", "bottom", "left", "right" | "bottom" |
Only matters when showZoomer: true. Which side of the
stage the slider sits on — purely a placement choice, identical zoom
behavior in every position. |
filters.* | — | see Filters | — | Initial brightness/contrast/saturation/grayscale/sepia values —
same shape as setFilters() takes. See
Filters. |
uploader | function | (blob, options & {url}) => Promise<unknown> | none — falls back to the built-in FormData/fetch uploader | Overrides how upload() actually sends the crop — plug
in your own function (e.g. to hit a presigned-URL flow or a GraphQL
mutation) instead of the default FormData POST. Can also be overridden
per-call via UploadOptions.uploader. |
autoSizeStage: when true
(default), the stage sizes itself to the frame's dimensions plus a 20px
margin on each axis — no CSS required on the container. Set
false to have the stage fill its container instead (100%
width/height via CSS), for embedding in a layout where you want to control
the stage's size directly.
load() options
Lets you open an image already zoomed/panned/rotated a certain way, instead of always starting from scratch — useful for restoring a previously-saved crop state.
| Option | Type | Valid values | Default | Description |
|---|---|---|---|---|
zoom | number | clamped to [minZoom, maxZoom] | minZoom |
Starting zoom level, as if setZoom() had been called
right after loading. |
offset.x / offset.y | number | clamped so the frame stays covered | 0 |
Starting pan position, in pixels from center. Clamped automatically so the frame can never show past the image's edge. |
rotation | number (degrees) | snapped to the nearest 90° | 0 |
Starting rotation, as if rotate() had already been
called that many degrees. |
flip.horizontal / flip.vertical | boolean | true, false | false |
Starting flip state, as if flipHorizontal()/flipVertical()
had already been called. |
export() / upload() options
upload(url, options) takes everything export()
does, plus a few upload-only fields.
| Option | Type | Valid values | Default | Description |
|---|---|---|---|---|
type | string | "base64", "blob", "canvas" | "base64" |
What shape the result comes back as: a data URL string
(drop straight into an <img src>), a Blob
(for FormData/upload()), or a raw
HTMLCanvasElement (for further canvas manipulation). |
format | string | "image/jpeg", "image/png", "image/webp" | "image/png" |
Output image format. A "circle" or
"rounded-rectangle" frame exported as
"image/jpeg" warns and renders solid black outside the
shape — JPEG has no alpha channel. Use PNG/WebP for a transparent
crop. |
quality | number | 0–1 (jpeg/webp only) | browser default | Compression quality — lower means smaller file size, more visible artifacts. Ignored for PNG (lossless). |
width | number | any positive pixel size | frame width | Output pixel width. Set this higher than the frame's own width to export at a larger resolution than what's shown on screen (e.g. export a 800px avatar from a 200px on-screen frame). |
height | number | any positive pixel size | frame height | Output pixel height. Same idea as width. |
fieldName (upload only) | string | any | "file" |
The FormData field name the blob is attached under —
match whatever your server endpoint expects (e.g. "avatar"). |
fileName (upload only) | string | any | "crop.<ext>", from format |
The filename the server sees for the uploaded file. |
extraFields (upload only) | object | any Record<string,string> | {} |
Extra plain-string fields to send alongside the file in the same
request — e.g. { userId: "42" }. |
fetchOptions (upload only) | object | any valid fetch init | {} |
Merged into the underlying fetch() call — use it for
headers like Authorization, or credentials:
"include" for cookies. method/body are
always overridden by upload() itself. |
uploader (upload only) | function | same shape as the constructor option | the constructor's uploader, or the built-in one |
Overrides the upload implementation for this one call only. |
frame.shape: "circle" or
"rounded-rectangle" + format: "image/jpeg"
logs a console.warn — JPEG has no alpha channel, so the area
outside the shape renders solid black instead of transparent. Use
"image/png" or "image/webp" for a crop with a
transparent background.