A modern React wrapper for CropperJS 2.x
A TypeScript-first React library that provides idiomatic React bindings for CropperJS 2.x web components.
- ✅ CropperJS 2.x Support - Built for the latest CropperJS web components
- ✅ React 19 Compatible - Full support for React 19.x
- ✅ TypeScript First - Complete type safety with strict mode
- ✅ Three APIs - Component, simplified hook, and advanced hook
- ✅ Modern Architecture - Built on web standards
- ✅ Fully Tested - 96%+ test coverage with 55 passing tests
- ✅ Interactive Docs - Storybook documentation
The original react-cropper library only supports CropperJS 1.x. CropperJS 2.x is a complete rewrite using modern web components, requiring new React bindings. This library provides three flexible APIs to work with CropperJS 2.x in React applications.
npm install @imerljak/react-cropper-2 cropperjs@2The simplest way to use the library:
import { useRef } from 'react';
import { Cropper, type CropperRef } from '@imerljak/react-cropper-2';
function App() {
const cropperRef = useRef<CropperRef>(null);
const handleGetBounds = () => {
const bounds = cropperRef.current?.getBounds();
console.log('Crop bounds:', bounds);
};
return (
<div>
<Cropper
ref={cropperRef}
src="/image.jpg"
alt="Crop me"
crossOrigin="anonymous"
aspectRatio={16 / 9}
onReady={() => console.log('Cropper ready!')}
onChange={(e) => console.log('Crop changed:', e.detail.bounds)}
/>
<button onClick={handleGetBounds}>Get Bounds</button>
<button onClick={() => cropperRef.current?.reset()}>Reset</button>
</div>
);
}For more control and composition:
import { useCropper } from '@imerljak/react-cropper-2';
function App() {
const { renderCropper, bounds, reset, clear, getCroppedCanvas } = useCropper({
src: '/image.jpg',
alt: 'Crop me',
crossOrigin: 'anonymous',
aspectRatio: 16 / 9,
onReady: () => console.log('Ready!'),
onChange: (e) => console.log('Changed:', e.detail),
});
const handleExport = async () => {
const canvas = await getCroppedCanvas();
const url = canvas?.toDataURL('image/png');
// Use the cropped image
};
return (
<div>
{renderCropper({ style: { maxHeight: '500px' } })}
<div>
{bounds && <pre>{JSON.stringify(bounds, null, 2)}</pre>}
</div>
<button onClick={reset}>Reset</button>
<button onClick={clear}>Clear</button>
<button onClick={handleExport}>Export</button>
</div>
);
}For maximum control, use the advanced hook:
import { useCropperAdvanced } from '@imerljak/react-cropper-2';
function App() {
const {
canvasRef,
selectionRef,
bounds,
isReady,
getBounds,
setBounds,
reset,
getCroppedCanvas,
} = useCropperAdvanced({
autoInitialize: true,
onReady: (canvas) => console.log('Ready!', canvas),
onChange: (e) => console.log('Changed:', e.detail),
});
return (
<div>
<cropper-canvas ref={canvasRef} background>
<cropper-image
src="/image.jpg"
alt="Crop me"
crossorigin="anonymous"
rotatable
scalable
translatable
/>
<cropper-selection
ref={selectionRef}
aspect-ratio={16 / 9}
initial-coverage={0.8}
movable
resizable
zoomable
outlined
>
<cropper-grid role="grid" bordered covered />
<cropper-crosshair centered />
<cropper-handle action="move" theme-color="rgba(255, 255, 255, 0.35)" />
<cropper-handle action="n-resize" />
<cropper-handle action="e-resize" />
<cropper-handle action="s-resize" />
<cropper-handle action="w-resize" />
<cropper-handle action="ne-resize" />
<cropper-handle action="nw-resize" />
<cropper-handle action="se-resize" />
<cropper-handle action="sw-resize" />
</cropper-selection>
</cropper-canvas>
<button onClick={() => setBounds({ x: 50, y: 50, width: 200, height: 200 })}>
Set Custom Bounds
</button>
<button onClick={reset}>Reset</button>
</div>
);
}| Prop | Type | Description |
|---|---|---|
src |
string |
Image URL (required) |
alt |
string |
Image alt text |
crossOrigin |
'anonymous' | 'use-credentials' |
CORS setting |
aspectRatio |
number |
Aspect ratio constraint (e.g., 16/9) |
initialCoverage |
number |
Initial crop coverage (0-1) |
background |
boolean |
Show background (default: true) |
rotatable |
boolean |
Enable rotation (default: true) |
scalable |
boolean |
Enable scaling (default: true) |
translatable |
boolean |
Enable translation (default: true) |
movable |
boolean |
Enable moving selection (default: true) |
resizable |
boolean |
Enable resizing selection (default: true) |
zoomable |
boolean |
Enable zooming (default: true) |
outlined |
boolean |
Show selection outline (default: true) |
onReady |
(canvas: CropperCanvasElement) => void |
Callback when ready |
onChange |
CropperEventHandler |
Callback when crop changes |
onCropStart |
CropperEventHandler |
Callback when crop starts |
onCropMove |
CropperEventHandler |
Callback during crop movement |
onCropEnd |
CropperEventHandler |
Callback when crop ends |
interface CropperRef {
getCanvas: () => CropperCanvasElement | null;
getBounds: () => CropperBounds | null;
setBounds: (bounds: Partial<CropperBounds>) => void;
reset: () => void;
clear: () => void;
getCroppedCanvas: (options?) => Promise<HTMLCanvasElement | null>;
}Simplified hook that handles rendering for you.
const {
renderCropper,
bounds,
isReady,
getBounds,
setBounds,
reset,
clear,
getCanvas,
getSelection,
getCroppedCanvas,
} = useCropper(options);Advanced hook for maximum control. You manage rendering yourself.
const {
canvasRef,
selectionRef,
bounds,
isReady,
getBounds,
setBounds,
reset,
clear,
getCanvas,
getSelection,
getCroppedCanvas,
} = useCropperAdvanced(options);Check out the interactive Storybook documentation with live examples for:
- Basic cropping
- Different aspect ratios
- Custom styling
- Event handling
- All three API approaches
- Advanced configurations
# Install dependencies
npm install
# Start example app
npm run dev
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build library
npm run build
# Start Storybook
npm run storybook
# Build Storybook
npm run build-storybookreact-cropper-2/
├── src/
│ ├── components/
│ │ ├── Cropper.tsx # Main component
│ │ └── Cropper.test.tsx # Component tests
│ ├── hooks/
│ │ ├── useCropper.tsx # Simplified hook
│ │ ├── useCropper.test.ts # Hook tests
│ │ ├── useCropperAdvanced.ts # Advanced hook
│ │ └── useCropperAdvanced.test.ts
│ ├── types/
│ │ ├── index.ts # TypeScript types
│ │ └── jsx.d.ts # JSX declarations
│ ├── stories/ # Storybook stories
│ └── index.ts # Main export
├── examples/basic/ # Example application
├── tests/setup.ts # Test configuration
└── dist/ # Build output
- React 18.0.0 or higher (works with React 19)
- CropperJS 2.0.1 or higher
- TypeScript 5.6+ (for TypeScript projects)
Supports all modern browsers that support:
- ES2020
- Web Components (Custom Elements)
- ES Modules
Contributions are welcome! Please feel free to submit issues and pull requests.
MIT © 2025
- CropperJS - The underlying cropping library
- react-cropper - Inspiration from the v1 wrapper
- CropperJS - The underlying library (v2.x)
- react-cropper - React wrapper for CropperJS v1.x