react-avatar-editor, react-cropper, and react-image-crop are React libraries that enable image cropping functionality in web applications. They allow users to select a region of an image, adjust boundaries, enforce aspect ratios, and export the cropped result. Each takes a different technical approach: react-avatar-editor uses HTML canvas, react-cropper wraps the vanilla Cropper.js library, and react-image-crop implements cropping purely with React and CSS. These differences affect performance, customization, maintainability, and ease of integration.
When you need to let users crop images in a React app — for profile pictures, content uploads, or design tools — you have several solid options. The three most common libraries are react-avatar-editor, react-cropper, and react-image-crop. They all solve the same basic problem but with very different approaches, APIs, and trade-offs. Let’s break them down from an engineering perspective.
Each library uses a fundamentally different rendering strategy, which affects performance, customization, and integration.
react-avatar-editor uses the <canvas> element under the hood. It draws the image onto a canvas and manipulates it directly using 2D context APIs. This gives you pixel-level control and makes exporting easy, but limits styling flexibility.
import AvatarEditor from 'react-avatar-editor';
function Editor() {
return (
<AvatarEditor
image="/photo.jpg"
width={250}
height={250}
border={50}
color={[255, 255, 255, 0.6]} // RGBA
scale={1.2}
rotate={0}
/>
);
}
react-cropper wraps the popular Cropper.js library (a vanilla JavaScript tool) in a React component. It manipulates the DOM directly using absolute-positioned overlays on top of an <img> tag. This means it’s not "React-native" — it uses refs and side effects heavily.
import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
function Editor() {
const cropperRef = useRef(null);
const getCropData = () => {
const imageElement = cropperRef?.current;
const cropper = imageElement?.cropper;
console.log(cropper.getCroppedCanvas().toDataURL());
};
return (
<>
<Cropper
src="/photo.jpg"
style={{ height: 400, width: '100%' }}
initialAspectRatio={1}
guides={false}
ref={cropperRef}
/>
<button onClick={getCropData}>Crop</button>
</>
);
}
react-image-crop is built entirely with React and CSS — no canvas, no external DOM manipulation. It uses absolutely positioned <div> elements to draw the crop area over an <img>. This makes it lightweight and easier to theme with standard CSS.
import React, { useState } from 'react';
import ReactCrop from 'react-image-crop';
import 'react-image-crop/dist/ReactCrop.css';
function Editor() {
const [crop, setCrop] = useState({ aspect: 1 });
return (
<ReactCrop
src="/photo.jpg"
crop={crop}
onChange={c => setCrop(c)}
/>
);
}
How each library enforces aspect ratios and minimum/maximum sizes varies significantly.
react-avatar-editor only supports fixed width and height. You define exact pixel dimensions, and the crop area is always that size. There’s no dynamic resizing by the user — they can only move and zoom the image within the fixed frame.
// Fixed 300x300 crop area — user cannot change shape
<AvatarEditor width={300} height={300} />
react-cropper offers full control: freeform, fixed aspect ratio, min/max dimensions, and even grid snapping. You configure this via props like aspectRatio, minWidth, viewMode, etc.
<Cropper
aspectRatio={16 / 9}
minWidth={200}
minHeight={100}
viewMode={1} // restrict crop box to container
/>
react-image-crop supports both freeform and fixed aspect ratios, and lets users resize the crop box unless you lock it. You control behavior via the crop state object.
// Allow user to resize, but keep 1:1 aspect
const [crop, setCrop] = useState({ unit: '%', width: 50, aspect: 1 });
<ReactCrop crop={crop} onChange={setCrop} />
Getting the final cropped image out is critical — and the methods differ.
react-avatar-editor provides a direct method to get a canvas reference, then export as data URL or blob:
const editorRef = useRef(null);
const handleSave = () => {
if (editorRef.current) {
const canvas = editorRef.current.getImage();
const dataUrl = canvas.toDataURL();
// Or: canvas.toBlob(blob => upload(blob));
}
};
<AvatarEditor ref={editorRef} />
react-cropper requires accessing the underlying Cropper.js instance through a ref:
const cropperRef = useRef(null);
const handleSave = () => {
const cropper = cropperRef.current?.cropper;
if (cropper) {
const canvas = cropper.getCroppedCanvas();
const dataUrl = canvas.toDataURL('image/jpeg');
}
};
<Cropper ref={cropperRef} />
react-image-crop doesn’t include export logic — you must implement cropping yourself using a canvas:
import { getCroppedImg } from './utils'; // You write this
const [completedCrop, setCompletedCrop] = useState(null);
useEffect(() => {
if (completedCrop?.width && completedCrop?.height) {
getCroppedImg(imageRef.current, completedCrop)
.then(canvas => canvas.toDataURL());
}
}, [completedCrop]);
<ReactCrop onComplete={c => setCompletedCrop(c)} />
The library provides a helper example in its docs, but you own the implementation.
react-avatar-editor is hard to style beyond background color and border size because it’s canvas-based. You can’t use CSS to change handles or overlay appearance.
react-cropper inherits Cropper.js’s extensive but non-React styling system. You override CSS classes like .cropper-view-box or .cropper-face. This works but feels disconnected from your component tree.
react-image-crop exposes CSS classes you can target (e.g., .ReactCrop__crop-selection), and since it’s pure React + CSS, you can easily wrap it in styled-components or pass custom class names.
As of 2024:
react-avatar-editor is deprecated. Its npm page states: "This package has been deprecated. Please use @davidtheclark/react-avatar-editor instead." However, the suggested fork hasn’t gained traction and lacks recent updates. Avoid in new projects.react-cropper is actively maintained and tracks updates to Cropper.js.react-image-crop is actively maintained, with regular releases and TypeScript support.react-cropper if:react-image-crop if:react-avatar-editor if:| Feature | react-avatar-editor | react-cropper | react-image-crop |
|---|---|---|---|
| Rendering | Canvas | DOM (Cropper.js wrapper) | Pure React + CSS |
| User Resizing | ❌ Fixed frame only | ✅ Full control | ✅ Configurable |
| Aspect Ratio Lock | ❌ (Fixed dims only) | ✅ Yes | ✅ Yes |
| Export Built-in | ✅ Direct canvas access | ✅ Via Cropper.js API | ❌ Manual implementation |
| Styling Flexibility | ❌ Very limited | ✅ Via global CSS | ✅ Component-scoped CSS |
| Maintenance Status | ⚠️ Deprecated | ✅ Active | ✅ Active |
| Bundle Impact | Medium | Large (includes Cropper.js) | Small |
For new projects, choose between react-cropper and react-image-crop based on your needs:
react-cropper.react-image-crop.And do not use react-avatar-editor in any new codebase — its deprecation makes it a technical liability.
Choose react-cropper when you need a mature, feature-rich cropping experience with advanced capabilities like rotation, zoom-to-pointer, grid guides, and precise aspect ratio controls. It’s ideal if your team is comfortable working with a DOM-manipulating wrapper around a vanilla JS library and can manage the larger bundle size. Ensure you’re prepared to customize appearance via global CSS overrides rather than React props.
Do not use react-avatar-editor in new projects — it is officially deprecated as noted on its npm page. While it offers simple canvas-based cropping with fixed dimensions, its lack of active maintenance and inability to support user-resizable crop areas make it unsuitable for modern applications. If you encounter it in legacy code, plan a migration to an alternative.
Choose react-image-crop for a lightweight, React-native solution that integrates cleanly with modern hooks-based architecture and TypeScript. It’s best when you want full control over the export pipeline (e.g., offloading cropping to a Web Worker) and prefer styling via standard CSS or CSS-in-JS. Accept that you’ll need to implement the canvas-based image extraction logic yourself, though the library provides clear examples.
Cropperjs as React component
Install via npm
npm install --save react-cropper
You need cropper.css in your project which is from cropperjs.
Since this project have dependency on cropperjs, it located in /node_modules/react-cropper/node_modules/cropperjs/dist/cropper.css or node_modules/cropperjs/dist/cropper.css for npm version 3.0.0 later
import React, { useRef } from "react";
import Cropper, { ReactCropperElement } from "react-cropper";
import "cropperjs/dist/cropper.css";
const Demo: React.FC = () => {
const cropperRef = useRef<ReactCropperElement>(null);
const onCrop = () => {
const cropper = cropperRef.current?.cropper;
console.log(cropper.getCroppedCanvas().toDataURL());
};
return (
<Cropper
src="https://raw.githubusercontent.com/roadmanfong/react-cropper/master/example/img/child.jpg"
style={{ height: 400, width: "100%" }}
// Cropper.js options
initialAspectRatio={16 / 9}
guides={false}
crop={onCrop}
ref={cropperRef}
/>
);
};
stringnull<Cropper src="http://fengyuanchen.github.io/cropper/images/picture.jpg" />
stringpicturestringnullhttps://github.com/fengyuanchen/cropperjs#dragmode
https://github.com/fengyuanchen/cropperjs#scalexscalex
https://github.com/fengyuanchen/cropperjs#scalexscaley
https://github.com/fengyuanchen/cropperjs#enable
https://github.com/fengyuanchen/cropperjs#disable
https://github.com/fengyuanchen/cropperjs#zoomto
https://github.com/fengyuanchen/cropperjs#rotateto
Accept all options in the docs as properties.
Use the cropper instance from onInitialized to access cropperjs methods
npm run build
npm start
MIT