react-image-crop: A Dependency-Free React Cropper With Controlled Crop State
A responsive image cropping tool for React
At a glance
- What is it?
- react-image-crop is a controlled React component for drawing and resizing a crop selection over any child element. It ships no UI for producing the cropped file, which is the trade-off to understand before adopting it.
- Who is it for?
- Adopt react-image-crop when you want the crop rectangle to live in your own React state and you are willing to write the canvas step that produces the final file. Do not adopt it if you need rotation, filtering or annotation in the same widget, or if you expect a ready-made image output.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 100 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem react-image-crop solves, and who it is for
Most image croppers are widgets: they own the image, the handles, the preview and the export button. react-image-crop inverts that. It renders a selection rectangle over whatever child you give it and reports the rectangle back through callbacks. Your component owns the crop state, your component draws the final image. The README states the component can crop anything, not just images, which follows from the same design: the rectangle is positioned over a wrapper element, and the child inside it is incidental.
That makes it a fit for product teams building a profile-picture uploader, a CMS thumbnail editor or a document scanner UI where the crop coordinates need to be stored, sent to a server, or restored later. The package is TypeScript-first, exports types such as Crop and PixelCrop, and the package.json declares react >=16.13.1 as a peer dependency. It has no runtime dependencies, and the README puts the bundle under 5KB gzipped. The README also states plainly that it does not work with IE, and the browserslist field targets the last three browser versions.
How the controlled crop model works
The mechanism is a controlled component. You hold crop in state and pass it in; the component calls onChange on every pointer move while the user drags or resizes, and you write the new value back. The README is explicit about the consequence: you must implement onChange and update your crop state, otherwise nothing will change. There is no internal state to fall back on.
Two coordinate systems are passed to the callbacks. crop uses pixels, percentCrop uses percentages. The README says they are interchangeable and that percent crops resist image or media resizing, which matters when a crop saved on a desktop layout has to be replayed against a smaller image on mobile. onComplete fires after a resize, drag or nudge rather than on every frame, so it is the cheaper place to persist a value. onDragStart and onDragEnd bracket the interaction for code outside the component. For rendering the result, the README points to a browser crop addon: cropToCanvas renders the completed crop to a canvas, and cropToImg returns an image object URL. The README describes cropToCanvas as the same idea as canvasPreview and cropToImg as the same idea as imgPreview from the demo helpers.
Installing react-image-crop and drawing a first crop
The README lists four package managers. Pick the one your project already uses; the package name is identical in all of them.
npm i react-image-crop --saveNext, import the component and its stylesheet. The stylesheet is a separate import, either the built CSS or the SCSS source, and the package.json exports both paths.
import ReactCrop from 'react-image-crop'
import 'react-image-crop/dist/ReactCrop.css'Now render the component around an img and keep the crop in state. The README's example is the smallest working version: start with crop undefined, pass it in, and set it from the onChange callback.
import ReactCrop, { type Crop } from 'react-image-crop'
function CropDemo({ src }) {
const [crop, setCrop] = useState<Crop>()
return (
<ReactCrop crop={crop} onChange={c => setCrop(c)}>
<img src={src} />
</ReactCrop>
)
}You should see the image with a draggable selection rectangle on top. If dragging does nothing, the state update is missing. To open with a preselected region, the README shows passing an object with unit set to '%' or 'px' plus x, y, width and height. The README warns that a manually set crop must be in bounds and correct for the aspect ratio, and points to the centerCrop and makeAspectCrop helpers for that. If you would rather not install anything, the CDN section gives a stylesheet link to unpkg and a script tag for index.umd.cjs, with the component exposed as ReactCrop.Component.
What the props do not do for you
The constraint that catches people is that react-image-crop exports a selection, not an image. Nothing in the component produces a cropped file. The README's FAQ answers how to generate a preview by pointing at cropToCanvas and cropToImg, but that is still your code in your component, and you are responsible for the canvas math, the output format and the upload. If you want a widget that hands back a finished blob, this is the wrong package.
The second limitation is the manual crop problem. When you set crop yourself, the README states you must ensure it is in bounds and correct to the aspect ratio, and notes that aspect ratios get tricky with percentages. Helpers exist, but the validation is not automatic. A crop restored from a database against a differently sized image can land outside the visible area unless you normalize it.
The third is interaction scope. aspect, minWidth, minHeight, maxWidth and maxHeight constrain the rectangle. keepSelection stops the selection being cleared by a click outside it. disabled and locked change what the user can do, and both add a class to the container so you can style the state. renderSelectionAddon lets you place a custom element inside the selection. ruleOfThirds and circularCrop change the overlay only. None of these touch the pixels. There is also no rotation, filtering or annotation in the component, which the README acknowledges by pointing readers to its sponsor Pintura for those features.
react-image-crop compared with react-easy-crop and react-advanced-cropper
The obvious alternatives are react-easy-crop and react-advanced-cropper, both of which appear in the searches people run around this project. The difference is where the crop state lives and what the component is willing to own.
react-easy-crop is built around a zoomable, pannable image inside a fixed crop frame, which suits a fixed output shape such as a square avatar. react-image-crop instead lets the selection itself be resized and moved, with free-form crops as the default and aspect as an optional constraint. If your UI is 'move and zoom the photo behind a fixed window', react-easy-crop matches that mental model more closely. If your UI is 'draw a box on the image and give me the coordinates', react-image-crop is the closer fit.
react-advanced-cropper targets a richer editing surface, with more built-in manipulation than a selection rectangle. That extra surface is also more code to learn and to keep updated. react-image-crop's bet is the opposite: under 5KB gzipped, no runtime dependencies, and a small prop list. The cost of that bet is that the surrounding workflow, the preview, the export and the persistence, is yours. The README's own framing supports this reading: when the component does not cover your requirements, it points to a separate commercial product rather than claiming to.
Maintenance, licence and the cost of upgrading
The repository is not archived, and the last push was on 2026-06-21, which is also the date of the 11.1.2 release. The previous release, 11.0.10, dates from 2025-04-14, so the cadence is irregular rather than continuous. Plan for occasional releases rather than a steady stream.
The package is licensed ISC, a permissive licence. It is your responsibility to confirm what that means for your distribution; this is not legal advice. Practically, the licence imposes little on a commercial product that bundles the component.
Upgrade cost is driven by the peer dependency and the build setup. The package.json declares react >=16.13.1, and the devDependencies pin React 19 and Vite 8 for development, so the library is built and linted against a modern toolchain while remaining installable on older React. The package ships as ESM with a UMD fallback: type is module, main points at dist/index.umd.cjs, module at dist/index.js, and exports maps both. The stylesheet is exported separately at dist/ReactCrop.css and src/ReactCrop.scss, with sideEffects listing *.css and *.scss so bundlers do not drop the styles. That separate stylesheet import is the most likely thing to break in a migration, since a bundler change or a CSS-in-JS switch can silently remove the crop overlay styling while the component still renders.
Editorial conclusion
Adopt react-image-crop when you want the crop rectangle to live in your own React state and you are willing to write the canvas step that produces the final file. Do not adopt it if you need rotation, filtering or annotation in the same widget, or if you expect a ready-made image output. Before committing, verify the two things the README leaves to you: that your crop state stays in bounds and at the right aspect ratio when set manually, and that you have a working canvas export path, since the library exports the selection, not the pixels.
Frequently asked questions
How do I install react-image-crop?
Install it with npm i react-image-crop --save, or the equivalent command for bun, pnpm or yarn, as listed in the README. You then import the component and import either react-image-crop/dist/ReactCrop.css or react-image-crop/src/ReactCrop.scss.
Why does dragging the react-image-crop selection do nothing?
The component is controlled, and the README states you must implement onChange and update your crop state, otherwise nothing will change. Passing crop without writing the callback result back into state leaves the rectangle frozen.
Does react-image-crop produce the cropped image file for me?
No. The component reports the selection through onChange and onComplete. The README's FAQ points to the browser crop addon, where cropToCanvas renders the completed crop to a canvas and cropToImg returns an image object URL, but that export step is code you write.
What is a react image crop alternative?
react-easy-crop and react-advanced-cropper are the alternatives that come up around this project. react-easy-crop centers on zooming and panning an image inside a fixed crop frame, while react-image-crop has you resize and move the selection itself and keeps the crop in your own state.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dominictobias-react-image-crop)
Community notes