ZaidanHomeDocsComponentsBlocksChartsTypesetCreate

Command Palette

Search for a command to run...

GitHub
New
Sections
  • Introduction
  • Components
  • Blocks
  • Installation
  • Customization
  • Dark Mode
  • Typeset
  • Zaidan Skills
  • FAQ
  • Roadmap
  • Changelog
Installation
  • Astro
  • Manual
  • Solid Start
  • TanStack Router
  • TanStack Start
  • Vite
Blocks
  • Data Grid
  • Event Calendar
  • Filters
  • Gantt
  • Image Crop
  • Kanban
  • Message Scroller
  • Questionnaire
  • Sortable
Components
  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Marker
  • Menubar
  • Message
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip

Image Crop

Drag, resize, then save a square crop.

1
import { Crop } from "lucide-solid";
2
import { createSignal, onCleanup, Show } from "solid-js";
3
4
import {
5
ImageCropCanvas,
6
ImageCropProvider,
7
type ImageCropResult,
8
useImageCrop,
9
} from "@/registry/kobalte/blocks/image-crop";
10
import { Button } from "~/components/ui/button";
11
12
const sampleImage = {
13
name: "portrait.svg",
14
src: `data:image/svg+xml,${encodeURIComponent(`
15
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="640" viewBox="0 0 960 640">
16
<defs>
17
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
18
<stop offset="0" stop-color="#bae6fd"/>
19
<stop offset="1" stop-color="#fef3c7"/>
20
</linearGradient>
21
</defs>
22
<rect width="960" height="640" fill="url(#bg)"/>
23
<circle cx="700" cy="150" r="70" fill="#f59e0b" opacity="0.8"/>
24
<path d="M0 420C160 340 320 400 480 340C660 272 800 320 960 260V640H0Z" fill="#0f172a" opacity="0.25"/>
25
<path d="M0 500C200 440 380 500 560 450C740 400 860 440 960 410V640H0Z" fill="#0f172a" opacity="0.35"/>
26
</svg>
27
`)}`,
28
type: "image/svg+xml",
29
};
30
31
export default function ImageCropDemo() {
32
const [result, setResult] = createSignal<ImageCropResult | null>(null);
33
34
const handleCrop = (nextResult: ImageCropResult) => {
35
result()?.revoke();
36
setResult(nextResult);
37
};
38
39
onCleanup(() => result()?.revoke());
40
41
return (
42
<ImageCropProvider defaultImage={sampleImage} onCrop={handleCrop}>
43
<div class="w-full max-w-2xl overflow-hidden rounded-xl border bg-background">
44
<ImageCropCanvas class="min-h-80" />
45
<div class="flex items-center justify-between gap-4 border-t p-4">
46
<Show
47
when={result()}
48
fallback={
49
<p class="text-muted-foreground text-sm">Drag, resize, then save a square crop.</p>
50
}
51
>
52
{(cropped) => (
53
<div class="flex items-center gap-3">
54
<img
55
alt="Cropped preview"
56
class="size-12 rounded-full border object-cover"
57
src={cropped().url}
58
/>
59
<p class="text-muted-foreground text-xs">
60
{cropped().width} x {cropped().height} output pixels
61
</p>
62
</div>
63
)}
64
</Show>
65
<CropButton />
66
</div>
67
</div>
68
</ImageCropProvider>
69
);
70
}
71
72
function CropButton() {
73
const crop = useImageCrop();
74
75
return (
76
<Button
77
disabled={!crop.image() || crop.isCropping()}
78
onClick={() => void crop.cropImage()}
79
type="button"
80
>
81
<Crop class="size-4" />
82
{crop.isCropping() ? "Cropping..." : "Crop"}
83
</Button>
84
);
85
}

Image Crop is a set of composable Solid primitives for building crop flows. ImageCropProvider owns the image, the crop geometry, and canvas export. ImageCropCanvas renders the image with a draggable, resizable, keyboard-accessible selection. useImageCrop exposes the same state to your own controls.

The block intentionally ships only the provider, canvas, hook, and types. Upload inputs, dialogs, action buttons, previews, and persistence stay in userland, so the installable surface remains small and your product composition stays free.

Installation

pnpm dlx shadcn@latest add @zaidan/image-crop
npx shadcn@latest add @zaidan/image-crop
yarn dlx shadcn@latest add @zaidan/image-crop
bunx --bun shadcn@latest add @zaidan/image-crop

Usage

1
import {
2
ImageCropCanvas,
3
ImageCropProvider,
4
useImageCrop,
5
} from "~/components/blocks/image-crop";
1
<ImageCropProvider
2
defaultImage={{ src: "/avatar.png", name: "avatar.png" }}
3
onCrop={(result) => {
4
// Upload result.blob, preview result.url, or store result.dataUrl.
5
}}
6
>
7
<ImageCropCanvas />
8
<CropActions />
9
</ImageCropProvider>

Controls read the crop state through useImageCrop from anywhere inside the provider.

1
function CropActions() {
2
const crop = useImageCrop();
3
4
return (
5
<button
6
type="button"
7
disabled={!crop.image() || crop.isCropping()}
8
onClick={() => void crop.cropImage()}
9
>
10
{crop.isCropping() ? "Cropping..." : "Save crop"}
11
</button>
12
);
13
}

Composition

ImageCropProvider
├── ImageCropCanvas
└── Your controls (useImageCrop)
  • ImageCropProvider is the headless root. It loads images, clamps crop geometry, scales the stage to the viewport, and exports the crop through an offscreen canvas.
  • ImageCropCanvas is the interactive stage. It measures itself with a ResizeObserver, renders the image at display scale, and hosts the draggable and resizable selection.
  • useImageCrop returns the context value for custom controls: upload buttons, geometry inputs, crop actions, and result previews.

The selection is a focusable button: arrow keys nudge the crop by one display pixel, and holding Shift nudges by ten.

Examples

Aspect Ratio

aspectRatio locks the selection shape. It defaults to 1 for square avatar crops; pass any positive ratio such as 16 / 9, or null for a freeform selection. The ratio applies to pointer resizing and to every setOptions update. Call resetCrop after changing the ratio to re-center the selection with the new shape.

1
import { createSignal, For } from "solid-js";
2
3
import {
4
ImageCropCanvas,
5
ImageCropProvider,
6
useImageCrop,
7
} from "@/registry/kobalte/blocks/image-crop";
8
import { Button } from "~/components/ui/button";
9
10
const sampleImage = {
11
name: "landscape.svg",
12
src: `data:image/svg+xml,${encodeURIComponent(`
13
<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="720" viewBox="0 0 1080 720">
14
<defs>
15
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
16
<stop offset="0" stop-color="#c7d2fe"/>
17
<stop offset="1" stop-color="#fce7f3"/>
18
</linearGradient>
19
</defs>
20
<rect width="1080" height="720" fill="url(#bg)"/>
21
<circle cx="820" cy="170" r="80" fill="#f97316" opacity="0.75"/>
22
<path d="M0 470C180 390 360 450 540 380C740 306 900 360 1080 300V720H0Z" fill="#1e1b4b" opacity="0.3"/>
23
</svg>
24
`)}`,
25
type: "image/svg+xml",
26
};
27
28
const ratios = [
29
{ label: "Square", value: 1 },
30
{ label: "4:3", value: 4 / 3 },
31
{ label: "16:9", value: 16 / 9 },
32
{ label: "Free", value: null },
33
] as const;
34
35
export default function ImageCropAspectRatio() {
36
const [aspectRatio, setAspectRatio] = createSignal<number | null>(1);
37
38
return (
39
<ImageCropProvider aspectRatio={aspectRatio()} defaultImage={sampleImage}>
40
<div class="w-full max-w-2xl overflow-hidden rounded-xl border bg-background">
41
<ImageCropCanvas class="min-h-80" />
42
<RatioControls aspectRatio={aspectRatio} onAspectRatioChange={setAspectRatio} />
43
</div>
44
</ImageCropProvider>
45
);
46
}
47
48
function RatioControls(props: {
49
aspectRatio: () => number | null;
50
onAspectRatioChange: (value: number | null) => void;
51
}) {
52
const crop = useImageCrop();
53
54
const selectRatio = (value: number | null) => {
55
props.onAspectRatioChange(value);
56
crop.resetCrop();
57
};
58
59
return (
60
<div class="flex flex-wrap items-center gap-2 border-t p-4">
61
<For each={ratios}>
62
{(ratio) => (
63
<Button
64
onClick={() => selectRatio(ratio.value)}
65
size="sm"
66
type="button"
67
variant={props.aspectRatio() === ratio.value ? "default" : "outline"}
68
>
69
{ratio.label}
70
</Button>
71
)}
72
</For>
73
</div>
74
);
75
}

Loading Images

Load images three ways: declaratively with defaultImage when the provider mounts, from an upload with setImageFromFile, or from any URL-like source with setImageFromSource. Files are read through object URLs that the provider revokes when the image is replaced or the provider unmounts. Non-image files are ignored by setImageFromFile.

Pick a file from your device or load the bundled sample image.

1
import { Image as ImageIcon, RefreshCcw } from "lucide-solid";
2
import { Show } from "solid-js";
3
4
import {
5
ImageCropCanvas,
6
ImageCropProvider,
7
useImageCrop,
8
} from "@/registry/kobalte/blocks/image-crop";
9
import { Button } from "~/components/ui/button";
10
11
const sampleImage = {
12
name: "sample.svg",
13
src: `data:image/svg+xml,${encodeURIComponent(`
14
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="680" viewBox="0 0 1024 680">
15
<defs>
16
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
17
<stop offset="0" stop-color="#a7f3d0"/>
18
<stop offset="1" stop-color="#e0f2fe"/>
19
</linearGradient>
20
</defs>
21
<rect width="1024" height="680" fill="url(#bg)"/>
22
<circle cx="260" cy="200" r="90" fill="#10b981" opacity="0.5"/>
23
<rect x="520" y="240" width="320" height="220" rx="24" fill="#0f172a" opacity="0.2"/>
24
</svg>
25
`)}`,
26
type: "image/svg+xml",
27
};
28
29
export default function ImageCropUpload() {
30
return (
31
<ImageCropProvider>
32
<div class="w-full max-w-2xl overflow-hidden rounded-xl border bg-background">
33
<UploadSurface />
34
</div>
35
</ImageCropProvider>
36
);
37
}
38
39
function UploadSurface() {
40
const crop = useImageCrop();
41
let inputRef: HTMLInputElement | undefined;
42
43
const handleFiles = (fileList: FileList | null) => {
44
const file = fileList?.[0];
45
46
if (!file) {
47
return;
48
}
49
50
void crop.setImageFromFile(file);
51
};
52
53
return (
54
<>
55
<input
56
ref={(element) => {
57
inputRef = element;
58
}}
59
accept="image/png,image/jpeg,image/gif,image/webp"
60
class="sr-only"
61
onChange={(event) => {
62
handleFiles(event.currentTarget.files);
63
event.currentTarget.value = "";
64
}}
65
type="file"
66
/>
67
<Show
68
when={crop.image()}
69
fallback={
70
<div class="flex min-h-80 flex-col items-center justify-center gap-4 p-8 text-center">
71
<p class="max-w-sm text-muted-foreground text-sm">
72
Pick a file from your device or load the bundled sample image.
73
</p>
74
<div class="flex flex-wrap items-center justify-center gap-2">
75
<Button onClick={() => inputRef?.click()} type="button">
76
<ImageIcon class="size-4" />
77
Select image
78
</Button>
79
<Button
80
onClick={() => void crop.setImageFromSource(sampleImage)}
81
type="button"
82
variant="secondary"
83
>
84
Use sample
85
</Button>
86
</div>
87
</div>
88
}
89
>
90
<ImageCropCanvas class="min-h-80" />
91
<div class="flex items-center justify-between gap-4 border-t p-4">
92
<p class="truncate text-muted-foreground text-sm">{crop.image()?.name}</p>
93
<Button onClick={() => inputRef?.click()} size="sm" type="button" variant="outline">
94
<RefreshCcw class="size-4" />
95
Replace
96
</Button>
97
</div>
98
</Show>
99
</>
100
);
101
}

Because export draws the image onto a canvas, remote sources must be same-origin or CORS-readable; blob and data URLs always work.

Controlled Crop Geometry

options exposes the crop rectangle in display pixels, and setOptions updates it with automatic clamping to the stage bounds and the active aspect ratio. Use the functional form to derive the next geometry from the previous one. resetCrop restores the centered default selection.

120 x 120 at (0, 0)

1
import { RotateCcw } from "lucide-solid";
2
3
import {
4
ImageCropCanvas,
5
ImageCropProvider,
6
useImageCrop,
7
} from "@/registry/kobalte/blocks/image-crop";
8
import { Button } from "~/components/ui/button";
9
10
const sampleImage = {
11
name: "grid.svg",
12
src: `data:image/svg+xml,${encodeURIComponent(`
13
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="640" viewBox="0 0 960 640">
14
<defs>
15
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
16
<stop offset="0" stop-color="#fde68a"/>
17
<stop offset="1" stop-color="#fca5a5"/>
18
</linearGradient>
19
</defs>
20
<rect width="960" height="640" fill="url(#bg)"/>
21
<circle cx="480" cy="320" r="120" fill="#7c2d12" opacity="0.3"/>
22
<rect x="120" y="120" width="200" height="140" rx="20" fill="#7c2d12" opacity="0.2"/>
23
<rect x="640" y="380" width="200" height="140" rx="20" fill="#7c2d12" opacity="0.2"/>
24
</svg>
25
`)}`,
26
type: "image/svg+xml",
27
};
28
29
export default function ImageCropControlled() {
30
return (
31
<ImageCropProvider defaultImage={sampleImage}>
32
<div class="w-full max-w-2xl overflow-hidden rounded-xl border bg-background">
33
<ImageCropCanvas class="min-h-80" />
34
<GeometryControls />
35
</div>
36
</ImageCropProvider>
37
);
38
}
39
40
function GeometryControls() {
41
const crop = useImageCrop();
42
43
const nudge = (deltaX: number, deltaY: number) => {
44
crop.setOptions((previous) => ({
45
...previous,
46
x: previous.x + deltaX,
47
y: previous.y + deltaY,
48
}));
49
};
50
51
const center = () => {
52
crop.setOptions((previous) => ({
53
...previous,
54
x: Math.floor((crop.displaySize().width - previous.width) / 2),
55
y: Math.floor((crop.displaySize().height - previous.height) / 2),
56
}));
57
};
58
59
return (
60
<div class="flex flex-wrap items-center justify-between gap-3 border-t p-4">
61
<p class="font-mono text-muted-foreground text-xs">
62
{crop.options().width} x {crop.options().height} at ({crop.options().x}, {crop.options().y})
63
</p>
64
<div class="flex flex-wrap items-center gap-2">
65
<Button onClick={() => nudge(-20, 0)} size="sm" type="button" variant="outline">
66
Left
67
</Button>
68
<Button onClick={() => nudge(20, 0)} size="sm" type="button" variant="outline">
69
Right
70
</Button>
71
<Button onClick={center} size="sm" type="button" variant="outline">
72
Center
73
</Button>
74
<Button onClick={crop.resetCrop} size="sm" type="button" variant="secondary">
75
<RotateCcw class="size-4" />
76
Reset
77
</Button>
78
</div>
79
</div>
80
);
81
}

Output Handling

cropImage draws the selected region to a canvas and resolves an ImageCropResult with a blob, a dataUrl, and an object url for previews. The same result is passed to onCrop. outputType and outputQuality control the encoding, and the original flag on the crop options switches between source-resolution output and display-resolution output.

No crop generated yet.

1
import { Download } from "lucide-solid";
2
import { createSignal, For, onCleanup, Show } from "solid-js";
3
4
import {
5
ImageCropCanvas,
6
ImageCropProvider,
7
type ImageCropResult,
8
useImageCrop,
9
} from "@/registry/kobalte/blocks/image-crop";
10
import { Button } from "~/components/ui/button";
11
import { Checkbox } from "~/components/ui/checkbox";
12
import { Label } from "~/components/ui/label";
13
14
type OutputType = "image/png" | "image/jpeg" | "image/webp";
15
16
const sampleImage = {
17
name: "sunset.svg",
18
src: `data:image/svg+xml,${encodeURIComponent(`
19
<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="620" viewBox="0 0 1080 620">
20
<defs>
21
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
22
<stop offset="0" stop-color="#fbcfe8"/>
23
<stop offset="1" stop-color="#fed7aa"/>
24
</linearGradient>
25
</defs>
26
<rect width="1080" height="620" fill="url(#bg)"/>
27
<circle cx="540" cy="420" r="130" fill="#ea580c" opacity="0.65"/>
28
<rect y="480" width="1080" height="140" fill="#7c2d12" opacity="0.35"/>
29
</svg>
30
`)}`,
31
type: "image/svg+xml",
32
};
33
34
const outputTypes: { label: string; value: OutputType }[] = [
35
{ label: "PNG", value: "image/png" },
36
{ label: "JPEG", value: "image/jpeg" },
37
{ label: "WebP", value: "image/webp" },
38
];
39
40
export default function ImageCropOutput() {
41
const [outputType, setOutputType] = createSignal<OutputType>("image/png");
42
const [result, setResult] = createSignal<ImageCropResult | null>(null);
43
44
const handleCrop = (nextResult: ImageCropResult) => {
45
result()?.revoke();
46
setResult(nextResult);
47
};
48
49
onCleanup(() => result()?.revoke());
50
51
return (
52
<ImageCropProvider
53
defaultImage={sampleImage}
54
onCrop={handleCrop}
55
outputQuality={0.85}
56
outputType={outputType()}
57
>
58
<div class="w-full max-w-2xl overflow-hidden rounded-xl border bg-background">
59
<ImageCropCanvas class="min-h-80" />
60
<OutputControls
61
onOutputTypeChange={setOutputType}
62
outputType={outputType}
63
result={result}
64
/>
65
</div>
66
</ImageCropProvider>
67
);
68
}
69
70
function OutputControls(props: {
71
onOutputTypeChange: (value: OutputType) => void;
72
outputType: () => OutputType;
73
result: () => ImageCropResult | null;
74
}) {
75
const crop = useImageCrop();
76
77
return (
78
<div class="space-y-4 border-t p-4">
79
<div class="flex flex-wrap items-center justify-between gap-3">
80
<div class="flex items-center gap-2">
81
<For each={outputTypes}>
82
{(type) => (
83
<Button
84
onClick={() => props.onOutputTypeChange(type.value)}
85
size="sm"
86
type="button"
87
variant={props.outputType() === type.value ? "default" : "outline"}
88
>
89
{type.label}
90
</Button>
91
)}
92
</For>
93
</div>
94
<div class="flex items-center gap-2">
95
<Label for="image-crop-output-original">Original resolution</Label>
96
<Checkbox
97
checked={crop.options().original}
98
id="image-crop-output-original"
99
onChange={(checked) =>
100
crop.setOptions((previous) => ({ ...previous, original: Boolean(checked) }))
101
}
102
/>
103
</div>
104
</div>
105
<div class="flex items-center justify-between gap-4">
106
<Show
107
when={props.result()}
108
fallback={<p class="text-muted-foreground text-sm">No crop generated yet.</p>}
109
>
110
{(cropped) => (
111
<p class="text-muted-foreground text-xs">
112
{cropped().width} x {cropped().height} pixels, {cropped().blob.type},{" "}
113
{Math.round(cropped().blob.size / 1024)} KB
114
</p>
115
)}
116
</Show>
117
<Button
118
disabled={!crop.image() || crop.isCropping()}
119
onClick={() => void crop.cropImage()}
120
type="button"
121
>
122
<Download class="size-4" />
123
{crop.isCropping() ? "Cropping..." : "Generate crop"}
124
</Button>
125
</div>
126
</div>
127
);
128
}

Every result holds an object URL. Call result.revoke() once the preview or upload is done to release it.

Accessibility

The crop selection is rendered as a button labelled "Crop area". It participates in the tab order, shows a visible focus ring, and supports keyboard nudging with the arrow keys (Shift for larger steps). Resize handles are pointer affordances and stay hidden from assistive technology.

API Reference

ImageCropProvider

Provides crop state, image loading, crop generation, and configuration to child components.

PropTypeDefaultDescription
aspectRationumber | null1Crop aspect ratio. The default locks the crop to a square. Use null (or any non-positive value) for freeform crops.
defaultImageImageCropInitialImage-Optional image loaded when the provider mounts. Use same-origin, blob, or data URLs so canvas export stays readable.
onCrop(result: ImageCropResult) => void-Called after cropImage generates a result.
outputQualitynumber0.92Quality passed to canvas.toDataURL for JPEG and WebP outputs.
outputType"image/png" | "image/jpeg" | "image/webp""image/png"MIME type used for the generated crop.

ImageCropCanvas

Renders the current image, crop selection, drag interaction, resize handles, and keyboard nudging. It accepts all div props and must be rendered inside ImageCropProvider. Nothing renders until an image is loaded.

PropTypeDefaultDescription
showResizeHandlesbooleantrueShows or hides the visible corner and edge handles. The selection border stays resizable either way.

Data attributes for styling: data-slot="image-crop-canvas" on the viewport, data-slot="image-crop-stage" on the scaled image wrapper, and data-slot="image-crop-selection" on the selection button.

useImageCrop

Returns the crop context for custom controls and persistence flows. Throws when called outside ImageCropProvider.

PropertyTypeDescription
aspectRatioAccessor<number | null>The active aspect ratio, normalized to null when freeform.
cropImage() => Promise<ImageCropResult | null>Generates the crop. Resolves null without an image or while a crop is already running.
displaySizeAccessor<{ height: number; width: number }>Rendered image size in display pixels.
imageAccessor<ImageCropImage | null>The loaded image metadata.
isCroppingAccessor<boolean>Whether a crop is currently being generated.
optionsAccessor<ImageCropOptions>Current crop geometry in display pixels.
resetCrop() => voidRe-centers the crop using the current image and aspect ratio.
setImageFromFile(file: File) => Promise<void>Loads an uploaded image file. Non-image files are ignored.
setImageFromSource(source: ImageCropInitialImage) => Promise<void>Loads an image from a source object.
setOptions(value: ImageCropOptions | ((previous: ImageCropOptions) => ImageCropOptions)) => voidUpdates crop geometry with bounds and aspect-ratio clamping.

ImageCropOptions

The crop rectangle, expressed in display pixels of the scaled stage.

PropertyTypeDescription
heightnumberSelection height.
originalbooleanWhen true (the default), cropImage exports at the source image resolution; when false, it exports at the display-pixel selection size.
widthnumberSelection width.
xnumberSelection offset from the left edge of the stage.
ynumberSelection offset from the top edge of the stage.

ImageCropInitialImage

Source object accepted by defaultImage and setImageFromSource.

PropertyTypeDescription
namestringOptional display name. Defaults to "image".
srcstringImage URL. Same-origin, blob, or data URLs keep the export canvas readable.
typestringOptional MIME type. Defaults to "image/png".

ImageCropResult

Resolved by cropImage and passed to onCrop.

PropertyTypeDescription
blobBlobThe generated image blob, encoded as outputType.
dataUrlstringBase64 data URL from the crop canvas.
heightnumberOutput image height in pixels.
optionsImageCropOptionsCrop geometry at the moment of export.
revoke() => voidRevokes the object URL held by url. Call it when the result is no longer displayed.
urlstringObject URL for previewing or downloading the generated blob.
widthnumberOutput image width in pixels.

On This Page

  • Installation
  • Usage
  • Composition
  • Examples
    • Aspect Ratio
    • Loading Images
    • Controlled Crop Geometry
    • Output Handling
  • Accessibility
  • API Reference
    • ImageCropProvider
    • ImageCropCanvas
    • useImageCrop
    • ImageCropOptions
    • ImageCropInitialImage
    • ImageCropResult
Built by Kevin Abatan. The source code is available on GitHub.