eglador-ui-react-image-cropper
v1.0.0-alpha.2
Published
React Image Cropper component for Eglador UI
Downloads
257
Maintainers
Readme
eglador-ui-react-image-cropper
A modern, headless, compound-component image cropper for React — PayloadCMS-style crop UI on the client, Sharp-powered processing on the server. Styled with Tailwind CSS v4.
Features
- Compound API —
ImageCropper,ImageCropperCanvas,ImageCropperControls,ImageCropperZoom/Rotate/Flip/AspectRatio/FocalPoint/Output(composition-first, shadcn/ui style) - TypeScript-first — full type safety on crop state, focal point, transforms
- Interactive crop overlay — 8 resize handles, draggable crop area, rule-of-thirds grid, box-shadow overlay (no clip-path seam artifacts)
- Pointer Events API — works with mouse, touch and pen, single code path
- Aspect ratios — freeform, 1:1, 4:3, 3:2, 16:9, 2:3, 9:16 (or any custom number); pixel-aware percent math handles non-square images correctly
- Zoom — CSS
transform: scale()GPU-accelerated, slider + scroll-to-zoom - Rotate — slider + 90° step buttons, full -180° to 180° range
- Flip — horizontal and vertical
- Focal point — click-to-set crosshair indicator (PayloadCMS-style focal point for responsive crops)
- Output preview — live canvas-rendered crop with format/quality controls; emits
Blob+dataUrl useImageCropperhook — build fully custom controls- Sharp server utilities —
cropImage,applyTransforms,generateSizesexported from/serversub-path - Auto-reset on src change — switching images cleanly resets crop/zoom/rotation/flip
Installation
npm install eglador-ui-react-image-cropperPeer dependencies: react >= 18 · react-dom >= 18 · tailwindcss ^4 · sharp >= 0.33 (optional — only required for /server utilities)
Setup
Add the following to your global stylesheet so Tailwind picks up the component classes:
@import "tailwindcss";
@source "../node_modules/eglador-ui-react-image-cropper";The @source path is relative to the CSS file location:
| Framework | CSS file location | Path |
|---|---|---|
| Next.js (App Router) | app/globals.css | ../node_modules/eglador-ui-react-image-cropper |
| Next.js (src/) | src/app/globals.css | ../../node_modules/eglador-ui-react-image-cropper |
| Vite | src/index.css | ../node_modules/eglador-ui-react-image-cropper |
Quick Start
import {
ImageCropper,
ImageCropperCanvas,
ImageCropperControls,
ImageCropperZoom,
ImageCropperRotate,
ImageCropperFlip,
ImageCropperAspectRatio,
ImageCropperFocalPoint,
ImageCropperOutput,
type CropResult,
} from "eglador-ui-react-image-cropper";
export function ProfileImageEditor({ src }: { src: string }) {
return (
<ImageCropper
src={src}
aspectRatio={1}
onChange={(result: CropResult) => {
console.log(result.crop, result.focalPoint, result.zoom);
}}
>
<ImageCropperCanvas className="h-[480px] rounded-lg" />
<ImageCropperAspectRatio />
<ImageCropperControls>
<ImageCropperZoom />
<ImageCropperRotate />
<ImageCropperFlip />
<ImageCropperFocalPoint />
</ImageCropperControls>
<ImageCropperOutput width={400} height={400} />
</ImageCropper>
);
}API
Components
| Component | Purpose |
|---|---|
| ImageCropper | Root provider. Accepts src, aspectRatio, initialCrop, initialFocalPoint, onChange, etc. |
| ImageCropperCanvas | Interactive crop surface — image + draggable crop overlay + 8 resize handles. |
| ImageCropperControls | Flex container for control bar (<Zoom>, <Rotate>, <Flip>, …). |
| ImageCropperZoom | Range slider + reset button. min/max/step props. |
| ImageCropperRotate | Range slider + 90° step buttons + reset. |
| ImageCropperFlip | Horizontal and vertical flip toggles. |
| ImageCropperAspectRatio | Ratio selector (default: Free, 1:1, 4:3, 3:2, 16:9, 2:3, 9:16). Pass ratios to override. |
| ImageCropperFocalPoint | Toggles focal-point mode — click on canvas to set focus point. |
| ImageCropperOutput | Live canvas preview of cropped result. Emits Blob + dataUrl via onGenerate. |
Hook
| Export | Returns |
|---|---|
| useImageCropper() | { src, crop, setCrop, focalPoint, setFocalPoint, zoom, setZoom, rotation, setRotation, flipX, setFlipX, flipY, setFlipY, aspectRatio, setAspectRatio, dimensions, getCropResult, ... } |
ImageCropper props
| Prop | Type | Description |
|---|---|---|
| src | string | Image URL or data URL (required) |
| aspectRatio | number \| undefined | Pixel aspect ratio (e.g. 16/9). Omit for freeform. |
| initialCrop | Partial<CropArea> | Initial crop region in percent. Defaults to full image. |
| initialFocalPoint | Partial<FocalPoint> | Initial focal point (defaults to center). |
| initialZoom | number | Default 1 |
| initialRotation | number | Default 0 |
| minCropWidth | number | Minimum crop width in percent. Default 10. |
| minCropHeight | number | Minimum crop height in percent. Default 10. |
| onChange | (result: CropResult) => void | Fires on any state change |
ImageCropperCanvas props
| Prop | Type | Description |
|---|---|---|
| showGrid | boolean | Rule-of-thirds grid lines. Default true. |
| overlayColor | string | Currently always renders as black. |
| overlayOpacity | number | 0–1, default 0.5. |
Crop Data Model
The crop region uses percentages of the original image (0–100), matching the PayloadCMS data shape:
interface CropArea {
x: number;
y: number;
width: number;
height: number;
unit: "%" | "px";
}
interface FocalPoint {
x: number;
y: number;
}
interface CropResult {
crop: CropArea;
focalPoint: FocalPoint;
zoom: number;
rotation: number;
flipX: boolean;
flipY: boolean;
}Percent-based coordinates are resolution-independent — store them in your database and apply server-side with Sharp at any output size.
Server (Sharp) Utilities
Import from the /server sub-path on your Node backend:
import {
cropImage,
applyTransforms,
generateSizes,
type CropArea,
type FocalPoint,
type ImageSize,
} from "eglador-ui-react-image-cropper/server";cropImage(input, options)
Apply just the crop region (plus optional rotate/flip):
const buffer = await cropImage(inputBufferOrPath, {
crop,
imageWidth: 4000,
imageHeight: 3000,
rotation: 90,
flipX: false,
flipY: false,
});applyTransforms(input, options)
Crop + transform + resize + re-encode in a single pipeline:
const buffer = await applyTransforms(inputBuffer, {
crop,
imageWidth,
imageHeight,
rotation,
flipX,
flipY,
outputWidth: 800,
outputHeight: 800,
format: "webp",
quality: 85,
});generateSizes(input, sizes, defaults?)
PayloadCMS-style multi-size generation. The base crop is extracted once, then each size is derived from the cropped buffer. Focal point governs the extract region when target dimensions are given:
const sizes = await generateSizes(inputBuffer, [
{ name: "thumb", width: 200, height: 200, fit: "cover" },
{ name: "card", width: 600, height: 400, fit: "cover" },
{ name: "hero", width: 1920, height: 1080, fit: "cover" },
], {
crop,
focalPoint,
format: "webp",
quality: 80,
});
// sizes[0] => { name: "thumb", buffer, width, height, format, size }Custom Controls
Use useImageCropper() inside ImageCropper children to build your own UI:
import { useImageCropper } from "eglador-ui-react-image-cropper";
function CropInfo() {
const { crop, focalPoint, zoom } = useImageCropper();
return (
<div>
<p>{crop.width.toFixed(0)}% × {crop.height.toFixed(0)}%</p>
<p>Focal: {focalPoint.x.toFixed(0)}%, {focalPoint.y.toFixed(0)}%</p>
<p>Zoom: {zoom.toFixed(1)}x</p>
</div>
);
}
<ImageCropper src={src}>
<ImageCropperCanvas />
<CropInfo />
</ImageCropper>Compatibility
Works with any React-based framework: Next.js, Remix, Vite + React, Gatsby, etc.
Server utilities require a Node runtime (sharp is not browser-compatible). Use them in API routes, Server Actions, or background workers.
Development
npm install
npm run dev # tsup watch mode
npm run build # production build to dist/ (client + server entries)
npm run typecheck # tsc --noEmit
npm run storybook # Storybook dev (http://localhost:6006)
npm run build-storybook # static Storybook exportPublishing
Publishing is automated via GitHub Actions. When a GitHub Release is created, the package is published to npm.
- Update
versioninpackage.json - Commit and push
- Create a GitHub Release with a matching tag (e.g.
v1.0.0)
Author
Kenan Gündoğan — https://github.com/kenangundogan
Maintained under Eglador
License
MIT
