Headless-first image cropping and editing SDK for Svelte 5. Zero dependencies beyond Svelte.
<Cropper> — Lightweight crop component with handles, grid overlays, and transforms<ImageEditor> — Full-featured editor with plugin-driven toolbarcreateCropper and createImageEditor for fully custom UIprofilePicture, coverPhoto, productImagesvelte ^5.0.0 onlynpm install @we-are-singular/svelte-chop-chop
<script lang="ts">
import { Cropper } from '@we-are-singular/svelte-chop-chop';
import '@we-are-singular/svelte-chop-chop/themes/default';
</script>
<Cropper src="/photo.jpg" aspectRatio={16 / 9} style="height: 400px;" />
<script lang="ts">
import { ImageEditor } from '@we-are-singular/svelte-chop-chop';
import { pluginFilters } from '@we-are-singular/svelte-chop-chop/plugins/filters';
import { pluginFinetune } from '@we-are-singular/svelte-chop-chop/plugins/finetune';
import { pluginFrame } from '@we-are-singular/svelte-chop-chop/plugins/frame';
import { pluginWatermark } from '@we-are-singular/svelte-chop-chop/plugins/watermark';
import { pluginResize } from '@we-are-singular/svelte-chop-chop/plugins/resize';
import type { ExportResult } from '@we-are-singular/svelte-chop-chop';
import '@we-are-singular/svelte-chop-chop/themes/default';
function handleExport(result: ExportResult) {
const url = URL.createObjectURL(result.blob!);
const a = document.createElement('a');
a.href = url;
a.download = 'edited.jpg';
a.click();
URL.revokeObjectURL(url);
}
</script>
<ImageEditor
src="/photo.jpg"
plugins={[pluginFilters(), pluginFinetune(), pluginFrame(), pluginWatermark(), pluginResize()]}
onexport={handleExport}
style="height: 500px;"
/>
| Prop | Type | Default | Description |
|---|---|---|---|
src |
ImageSource |
— | URL, data URL, File, Blob, or HTMLImageElement |
aspectRatio |
number | { min?: number; max?: number } | null |
null |
Aspect ratio constraint |
sizeConstraints |
SizeConstraints |
— | Min/max width/height in pixels |
initialCropScale |
number |
1 |
Initial crop as fraction of image (0–1). 0.8 = 80% centered |
grid |
"none" | "rule-of-thirds" | "grid" | "golden-ratio" |
"rule-of-thirds" |
Grid overlay |
transitions |
boolean |
true |
CSS transitions |
stencil |
Component |
CropStencil |
Custom stencil component |
readOnly |
boolean |
false |
Disable interaction |
class / style |
string |
'' |
Root element styling |
Bindable: coordinates (CropCoordinates), transforms (TransformState)
Events: onchange, onready, onerror
import { createCropper } from "@we-are-singular/svelte-chop-chop/headless";
import { createImageEditor } from "@we-are-singular/svelte-chop-chop";
// Lightweight cropper (export uses full resolution — original image pixels)
const cropper = createCropper({ aspectRatio: 16 / 9 });
await cropper.loadImage("/photo.jpg");
const result = await cropper.export({ format: "image/webp", quality: 0.9 });
// Full editor
const editor = createImageEditor({ plugins: [pluginFilters()] });
await editor.loadImage("/photo.jpg");
editor.applyFilter("clarendon");
editor.rotate(90);
const result = await editor.export({ format: "image/webp", quality: 0.9 });
// result.blob, result.dataURL, result.canvas, result.coordinates
| Import | Description |
|---|---|
@we-are-singular/svelte-chop-chop/plugins/filters |
16 Instagram-style color filter presets |
@we-are-singular/svelte-chop-chop/plugins/finetune |
Brightness, contrast, saturation, exposure, etc. |
@we-are-singular/svelte-chop-chop/plugins/frame |
Decorative frame at export (solid, line, hook) |
@we-are-singular/svelte-chop-chop/plugins/watermark |
Text watermark at export |
@we-are-singular/svelte-chop-chop/plugins/resize |
Output width/height controls |
import type { ChopPlugin } from "@we-are-singular/svelte-chop-chop";
const myPlugin: ChopPlugin = {
name: "my-plugin",
setup(ctx) {
ctx.registerAction({
id: "my-action",
label: "My Action",
group: "tabs",
execute: () => ctx.showPanel("my-panel"),
});
ctx.registerPostProcessor(async (drawCtx, canvas) => {
// draw on canvas at export time
});
return () => {
/* cleanup */
};
},
};
import { profilePicture, coverPhoto, productImage } from '@we-are-singular/svelte-chop-chop/presets';
// Spread into component props
<Cropper src="/photo.jpg" {...profilePicture} />
| Preset | Aspect | Max Size | Format |
|---|---|---|---|
profilePicture |
1:1 | 512px | JPEG |
coverPhoto |
16:9 | min 1200px wide | JPEG |
productImage |
1:1 | — | PNG |
@import "@we-are-singular/svelte-chop-chop/themes/default"; /* or dark, minimal */
Override any CSS custom property:
:root {
--chop-bg: #1a1a2e;
--chop-stencil-border: #e94560;
--chop-toolbar-active: #e94560;
}
Available variables: --chop-bg, --chop-canvas-bg, --chop-color, --chop-border-radius, --chop-stencil-border, --chop-stencil-border-active, --chop-grid-color, --chop-overlay, --chop-toolbar-bg, --chop-toolbar-color, --chop-toolbar-active, --chop-transition-duration, --chop-transition-easing
| Path | Description |
|---|---|
@we-are-singular/svelte-chop-chop |
Components, composables, types |
@we-are-singular/svelte-chop-chop/headless |
createCropper composable |
@we-are-singular/svelte-chop-chop/plugins |
All plugin factories |
@we-are-singular/svelte-chop-chop/presets |
Preset bundles |
@we-are-singular/svelte-chop-chop/themes/default |
Default CSS theme |
@we-are-singular/svelte-chop-chop/themes/dark |
Dark CSS theme |
@we-are-singular/svelte-chop-chop/themes/minimal |
Minimal CSS theme |
| Key | Action |
|---|---|
R / Shift+R |
Rotate 90° CW / CCW |
H / V |
Flip horizontal / vertical |
Ctrl+Z / Ctrl+Shift+Z |
Undo / Redo |
+ / - |
Zoom in / out |
0 |
Reset crop to image bounds |
Escape |
Reset all transforms |
Ctrl+F |
Toggle filters panel |
| Arrow keys | Move crop 1px (10px with Shift) |
MIT © We Are Singular