SaaS stacks
Aaron Brick12 min read5 views

react-easy-crop vs react-image-crop vs react-advanced-cropper: choosing a Next.js image cropper in 2026

Three React image croppers whose received wisdom is three months out of date. Read from the npm registry, the published tarballs and the GitHub API on 1 October 2026: the library everyone calls selection-only now writes the canvas code for you, the most downloaded one still ships zero canvas code, and the package npm reports as smallest is actually the heaviest.

Minimalist flat illustration of three nested image-crop selection rectangles with corner handles over a placeholder photo inside a thin browser frame, with three small option badges beside it in grey, charcoal and moss green.
Minimalist flat illustration of three nested image-crop selection rectangles with corner handles over a placeholder photo inside a thin browser frame, with three small option badges beside it in grey, charcoal and moss green.
On this page

Quick answer (October 2026). The received wisdom about these three is about three months out of date. Measured live from the npm registry, the published tarballs and the GitHub API on 1 October 2026: react-image-crop, the library every roundup describes as selection-only, began shipping its own canvas output helpers in v11.1.0 on 21 June 2026 and now hands you a finished image. react-easy-crop, the most downloaded of the three at 3,946,859 weekly installs, still ships zero canvas code, and so does its unreleased v7 canary. react-advanced-cropper, the least downloaded by a factor of nineteen, is the only one whose design has always been to give you a canvas.

So the question is not which is easiest. It is what you want back from the component: coordinates, a canvas, or a ready-made object URL.

react-easy-crop logo react-image-crop logo react-advanced-cropper logo

The axis that actually separates them

Every cropper has to answer one question: when the user stops dragging, what does your code receive?

Scroll to see more

react-easy-crop 6.2.3react-image-crop 11.1.2react-advanced-cropper 0.20.1
What you get backCrop rectangle onlyCrop rectangle, plus canvas and object-URL helpersA canvas, via getCanvas()
canvas in shipped code0 occurrences2 runtime, 3 in types96 runtime, 15 in types
Public exported namescomponent and props type2191
Type surface shipped7,192 B over 2 files8,478 B over 2 files43,240 B over 52 files
Weekly installs3,946,8593,045,055207,435
Runtime dependenciesnormalize-wheelnoneadvanced-cropper, classnames, tslib

Weekly figures are npm's own count for 23 to 29 September 2026. The occurrence counts are a case-insensitive scan of every .d.ts, .js and .mjs file in each published tarball, not a reading of the documentation.

That first row is the whole decision, and it is the row the popular roundups get wrong.

The claim that stopped being true in June 2026

If you search for a React cropper you will be told, repeatedly, that react-image-crop is a minimal selection overlay and that producing the actual cropped image is your job. That was true for years. It is not true now.

Version 11.1.0, published 21 June 2026, added two exported functions. From the shipped type definitions:

Scroll to see more

Exported functionParametersResolves to
cropToCanvasimage, canvas, crop, optional scale, optional rotatenothing; it draws into the canvas you pass
cropToImgimage, crop, optional scale, optional rotatea string, an object URL for the cropped image

Both are declared in the shipped .d.ts, typed against HTMLImageElement, HTMLCanvasElement and the library's own PixelCrop, and both return promises.

cropToImg creates a canvas for you, draws the crop with the scale and rotation applied, converts it with toBlob, and returns an object URL. It also revokes the previous object URL it handed out, which is the leak most hand-rolled versions of this code forget.

Scanning the tarballs version by version dates the change precisely:

Scroll to see more

VersionPublishedcropToCanvascropToImg
11.0.72024-09-09absentabsent
11.0.102025-04-14absentabsent
11.1.02026-06-21presentpresent
11.1.22026-06-21presentpresent

Three months is not long enough for the advice layer to catch up, which is why the listicles still describe the old behaviour.

The mirror image is react-easy-crop. Its documentation is famous for a getCroppedImg helper, and that helper is demo code in the docs, not an export. Scanning its tarball for canvas, drawImage, toBlob, toDataURL and OffscreenCanvas returns zero hits in both the runtime and the types. The same scan against the newest v7 canary, published 5 September 2026, also returns zero. Coordinates-only is a deliberate and continuing design choice there, not a gap waiting to be filled.

Weight, measured from the tarballs

Bundle size is the question people actually ask about croppers, and npm's headline number answers it badly.

Scroll to see more

Packagenpm unpackedReal ESM entryGzippedStylesheet
react-image-crop115,367 B19,684 B5,028 B4,791 B
react-easy-crop281,101 B38,213 B8,148 B1,578 B
react-advanced-cropper1,125,449 B81,758 B14,161 B1,191 to 2,622 B per theme
react-cropper20,486 B2,944 B, plus Cropper.js1,323 B, plus 23,294 B4,978 B

Read the first column on its own and react-cropper is the lightest option by a factor of five and react-advanced-cropper is a monster. Both readings are wrong.

react-advanced-cropper's 1.1 MB is 72 files of themes, source maps and multiple build targets. The thing your bundler actually pulls is 81,758 B, and 14,161 B gzipped.

react-cropper is the inverse and the more serious trap. It is a 2,944 B wrapper that declares cropperjs version 1.5.13 or compatible as a runtime dependency, so installing it pulls in Cropper.js 1.6.3, whose ESM entry is 108,396 B and 23,294 B gzipped. The honest total is roughly 111 KB raw and 24.6 KB gzipped, which makes the apparently smallest package the heaviest of the four.

These are upper bounds. All four ship ESM and mark themselves tree-shakeable or side-effect-scoped, so a real application will drop some of this. The ranking between them is what matters, and the ranking survives.

Maintenance, read properly

GitHub's pushed_at field is a poor maintenance signal because it counts a push to any branch, including an unattended bot.

cropperjs logo

Scroll to see more

Starspushed_atLast commit on default branchOpen issuesOpen PRs
react-image-crop4,1052026-06-212026-06-21685
react-easy-crop2,7792026-09-102026-07-2451
react-advanced-cropper8862026-07-252026-07-25141
react-cropper2,0762023-09-112023-09-11312

Two notes on reading that table.

First, react-easy-crop's pushed_at is nine weeks newer than its default branch, and the difference is not neglect. The 10 September push was a Dependabot bump of vitest. The real work is on a branch called feat/hooks-react-compiler-v7, last touched 5 September 2026, and it is being published to npm under the canary tag. So the project is not drifting, it is being rewritten one branch over. A naive reading of either field alone gets this wrong in opposite directions.

Second, GitHub's open_issues_count includes pull requests. react-image-crop's raw count is 73; separating them gives 68 issues and 5 PRs. react-cropper's raw count of 15 is 3 issues and 12 pull requests, and eleven of its fourteen branches are Dependabot or Renovate. That is the signature of a repository nobody is steering.

If you are on React 18 and planning ahead, note that the v7 canary of react-easy-crop declares a peer dependency of React 19.2 or newer, where the released v6 line accepts React 16.4 and above. The released versions of all three are permissive: react-image-crop wants React 16.13.1 or newer, react-advanced-cropper 16.8.0 or newer. None of them blocks React 19 today.

Licences, which are not all MIT

This is the kind of detail a dependency policy review catches late.

  • react-easy-crop ships a 1,073 B LICENSE, plain MIT.
  • react-image-crop is ISC, not MIT, in a 784 B LICENSE.md. ISC is permissive and functionally equivalent for most purposes, but it is a different identifier and some automated policy checks are configured with an explicit allowlist.
  • react-advanced-cropper is the interesting one. npm's one-word field says MIT. GitHub's licence detector says NOASSERTION. The actual 1,257 B file opens with a scope line before the MIT text: the source code is MIT, the documentation content belongs to the author, and the photos belong to their respective owners.

The MIT body underneath is unmodified, so this does not restrict shipping the library. It matters only if you copy the documentation prose or the demo photographs into your own material. The reason the two registries disagree is exactly that preamble, and a one-word metadata field cannot express a condition. Worth knowing, not worth worrying about.

One further note if you go down the react-advanced-cropper path: its core package, advanced-cropper 0.17.1, ships no licence file at all in its tarball. Its only grant is the license field in package.json.

The roundups that rank these libraries almost always include two more, and both need a caveat.

react-cropper is a React wrapper around Cropper.js, at 517,476 weekly installs. Its last release was April 2023 and its last commit September 2023. It pins Cropper.js to the version 1 line while Cropper.js shipped 2.2.0 in August 2026, a rewrite built on custom elements that the wrapper's component model does not fit.

The tempting conclusion is that this leaves you stranded, and that is not what the registry shows. Cropper.js published 1.6.3 on 23 August 2026, the same day as 2.2.0. The v1 line is still receiving releases. So react-cropper is a dormant wrapper over a maintained dependency, which is a milder problem than an abandoned stack, though it does mean the v2 rewrite is not reachable through it.

react-avatar-editor is 865,884 weekly installs and published v16.0.0 on 1 October 2026, so it is the most actively released package in this space. It is also a different tool: a fixed circular or square avatar editor rather than a general cropper. If you only need profile pictures it is a reasonable answer, and it is not a competitor to the three above.

How to choose

  • You want the component to produce the finished image and you want the smallest bundle: react-image-crop. Since 11.1.0 it does the canvas work, it is the lightest at 5,028 B gzipped, it has no runtime dependencies, and ISC is fine for almost every policy. The cost is 68 open issues and the largest backlog of the three.
  • You want the familiar pinch, zoom and rotate interaction and you are happy to own the output step: react-easy-crop. It is the most installed, it has the cleanest issue tracker by a wide margin, and you will copy about twenty lines of canvas code out of its documentation. Check the v7 branch before starting anything long-lived.
  • You want to build a cropper that does not look like a cropper, with custom stencils and your own interaction rules: react-advanced-cropper. The 91 exported names and 52 type files are the point, not bloat, and getCanvas() means you never write the output step. You are accepting the smallest community of the three and a 19-month-old core package.

If none of that decides it, pick on the first row of the first table. Everything else in this comparison is secondary to whether you want coordinates or an image.

For a worked example of the same measure-the-tarball approach applied to a different React decision, see our list virtualization comparison.

Method

Every figure above was read on 1 October 2026 from primary sources: registry.npmjs.org for versions, dependencies, peer ranges and unpacked sizes; the npm downloads API for install counts; the published .tgz tarballs for licence files, type definitions, stylesheet sizes and the occurrence scans; and the GitHub REST API for stars, branches, default-branch commits and the issue and pull-request split. Gzipped sizes are the ESM entry point named by each package's own module or exports field, compressed at level 9. No figure here was taken from another article.

Aaron Brick

Written by

Aaron Brick

Aaron Brick curates the ShipGarden gallery, where we test open-source building blocks so we can own the stack that funds the life.

Frequently asked questions

Does react-image-crop still require you to write the canvas code yourself?

No, not since version 11.1.0, published 21 June 2026. That release added two exported functions, cropToCanvas and cropToImg. cropToImg creates the canvas, applies your scale and rotation, converts the result with toBlob and returns an object URL, revoking the previous one it issued. Scanning the published tarballs version by version, 11.0.10 from April 2025 contains neither function and 11.1.0 contains both. Most comparison articles still describe the older behaviour.

Which of these three gives you the cropped image rather than just coordinates?

Two of them now. react-image-crop does, through cropToCanvas and cropToImg since 11.1.0. react-advanced-cropper always has, through getCanvas(). react-easy-crop does not: a case-insensitive scan of every shipped type and runtime file in version 6.2.3 finds zero occurrences of canvas, drawImage, toBlob, toDataURL or OffscreenCanvas, and the same scan of its v7 canary from 5 September 2026 also finds zero. The getCroppedImg function people associate with it is example code in its documentation, not an export.

Which one is actually the smallest?

react-image-crop, at 19,684 bytes for its ESM entry point and 5,028 bytes gzipped, with no runtime dependencies. react-easy-crop is 38,213 bytes and 8,148 gzipped. react-advanced-cropper is 81,758 bytes and 14,161 gzipped. Do not use npm's unpacked size for this: it reports react-advanced-cropper at 1.1 MB, which is mostly themes and build targets your bundler never loads.

Why does react-cropper look the smallest on npm when it is not?

Because npm's unpacked size measures only the package you install. react-cropper is a 2,944 byte wrapper, so it reports 20,486 bytes unpacked, but it declares Cropper.js version 1.5.13 or compatible as a runtime dependency. Installing it pulls Cropper.js 1.6.3, whose ESM entry is 108,396 bytes and 23,294 gzipped. The realistic total is about 111 KB raw and 24.6 KB gzipped, making the apparently lightest option the heaviest of the four.

Is react-cropper abandoned, given Cropper.js has moved to version 2?

The wrapper is dormant and its dependency is not. react-cropper last published in April 2023, last committed in September 2023, and eleven of its fourteen branches are automated dependency bots. But Cropper.js published 1.6.3 on 23 August 2026, the same day as 2.2.0, so the version 1 line the wrapper pins is still maintained. That is milder than an abandoned stack. The real limitation is that Cropper.js 2 is a custom-elements rewrite the wrapper's component model cannot reach.

Are all three MIT licensed?

No. react-easy-crop ships plain MIT. react-image-crop is ISC, which is permissive and broadly equivalent but is a different identifier, and some automated policy checks use an explicit allowlist. react-advanced-cropper is listed as MIT on npm and as NOASSERTION on GitHub, because its licence file opens with a scope line before the MIT text: the source code is MIT, the documentation content belongs to the author and the photos to their owners. The MIT body is unmodified, so this does not restrict shipping the library, only copying its documentation or demo images. Its core package, advanced-cropper 0.17.1, ships no licence file at all.

SaaS stacks

Sonner vs react-hot-toast vs react-toastify for Next.js in 2026

Three React toast libraries whose reputations are all slightly wrong. Read from the npm registry, the published tarballs, the GitHub API and the live shadcn registry on 23 September 2026: the most downloaded one is the heaviest, the one with the steepest search decline shipped a release seven days ago, and the oldest has the best accessibility.

12 min read107
SaaS stacks

tsup vs unbuild vs bunchee: TypeScript package bundlers in 2026

Three zero-config bundlers for shipping a TypeScript package, measured against live registry and repository state on 28 September 2026. One carries a maintainer deprecation notice, one has not released in over a year, and one requires Node 22.12. Plus what Vercel's turborepo actually uses, which is not what the usual citation says.

11 min read65