Library / SDK
Mawi137/ngx-image-cropper avatar
Mawi137/ngx-image-cropper

ngx-image-cropper: cropping images inside an Angular form

An image cropper for Angular

819 stars213 forksTypeScriptMIT

At a glance

What is it?
ngx-image-cropper is a standalone Angular component that lets a user crop an image before it is uploaded. It feeds output back as a blob, base64 or object URL, but its documentation and its own examples disagree about which one you receive.
Who is it for?
Adopt ngx-image-cropper if your Angular app needs a single, focused crop box tied to a file input, and you are willing to write the upload plumbing yourself. Skip it if you need rotation, filters or annotation, or if you want a component that does not constrain you to Angular 17.3 and up.
Can I use it commercially?
Yes. MIT 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 13 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 October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An Angular cropper that sits inside your own form

ngx-image-cropper exists for one job: letting a visitor frame an image before your backend receives it. It ships as a single Angular component named image-cropper, written in TypeScript, and it is aimed at teams that already own the form, the upload endpoint and the validation rules. The component does not upload anything on its own. It hands the cropped result back to your component through events, and your code decides whether to post it to a server or to preview it locally.

That boundary matters. Many cropper packages bundle their own widget design and their own modal. This one leaves the surrounding interface to you and concentrates on the crop rectangle itself, which means it fits into a multi step checkout or a profile editor without fighting the layout around it.

The package is published under the MIT licence, so the code can be used in commercial products with attribution, and the workspace repository includes an explicit copy step that places the README and the LICENSE file into the built package before publishing. If you plan to fork or rebrand the component, the licence does not stand in the way. It says nothing about the design assets you pair it with, so any styling you import separately remains your own responsibility.

Four ways to hand an image to the cropper

The component accepts the source image through four separate inputs, and the API table in the README is explicit that only one of them needs to be set. imageChangedEvent takes the change event straight from a standard file input. imageFile accepts a Blob, which is useful when your application already holds the file. imageBase64 lets you pass an encoded image as a string. imageURL accepts a link, and the documentation warns that a cross domain request will fail to load unless Cross-Origin Resource Sharing is allowed on the remote server.

That CORS warning is the one integration detail worth reading twice. The same origin case works without any server work, but pointing imageURL at a CDN or another host that does not send permissive CORS headers produces a blank cropper and a loadImageFailed event rather than a console error that names the cause.

The same four inputs are also the reset mechanism. The documentation states that setting imageChangedEvent or imageFile back to null resets the cropper, which gives you a clean way to clear a preview when the user picks a different file or cancels a dialog.

Installing the package and wiring the component for the first time

The install step is a single npm command, and the repository recommends saving it to your manifest so the version is tracked with the rest of your dependencies.

bash
npm install ngx-image-cropper --save

The component is a standalone component, so you import it directly into the class that renders the cropper rather than declaring it in a shared module.

typescript
import { ImageCropperComponent, ImageCroppedEvent, LoadedImage } from 'ngx-image-cropper';
import { DomSanitizer } from '@angular/platform-browser';

In the template you place a file input and bind its change event, then feed the cropper with the resulting event.

html
<input type="file" (change)="fileChangeEvent($event)" />

<image-cropper
    [imageChangedEvent]="imageChangedEvent"
    [maintainAspectRatio]="true"
    [aspectRatio]="4 / 3"
    format="png"
    (imageCropped)="imageCropped($event)"
    (imageLoaded)="imageLoaded($event)"
    (cropperReady)="cropperReady()"
    (loadImageFailed)="loadImageFailed()"
></image-cropper>

<img [src]="croppedImage" />

The handler for the change event only stores the event, and the binding passes that stored event to the cropper as an input.

typescript
    fileChangeEvent(event: Event): void {
        this.imageChangedEvent = event;
    }
    imageCropped(event: ImageCroppedEvent) {
      this.croppedImage = this.sanitizer.bypassSecurityTrustUrl(event.objectUrl);
      // event.blob can be used to upload the cropped image
    }

The DomSanitizer import is there because the preview URL is a blob URL, which Angular treats as untrusted until you mark it as such. If you only intend to upload the file and skip the preview, that binding is optional.

Blob, base64 or object URL: the output contract that contradicts itself

Here is the roughest edge in the project. The output input is documented as accepting blob or base64, with blob described as the most performant option and used as the default. The prose further down the same page says that every time the user releases the mouse the imageCropped event fires with the cropped image as a Base64 string in its payload. The example code, however, reads event.objectUrl for the preview and refers to event.blob in a comment for uploads. Three descriptions, two of them mentioning a blob based result.

The example code is the most reliable signal, because it is what a developer would run first, and the commented note about event.blob matches the default of the output input. The base64 sentence reads like text left over from an earlier version of the API. Treat it that way, but verify it in your own application before you build an upload pipeline on top of it.

The event surface around it is stable and small. imageCropped fires on every mouse release, which means it fires repeatedly while the user drags and adjusts, not once at the end. imageLoaded tells you when the source image is ready, cropperReady tells you when the cropper itself is mounted, and loadImageFailed reports a source image that could not be decoded, for example a cross domain image without CORS headers.

Aspect ratio, output format and the browsers that can decode them

Cropping geometry is controlled by aspectRatio, which defaults to 1 / 1, and by the maintainAspectRatio boolean, which the example sets to true so the crop box cannot be dragged out of proportion. Setting the ratio to something like 4 / 3 in the template gives you a landscape crop, and the fixed ratio is usually what an avatar or a banner upload needs.

The format input defaults to png and lists png, jpeg, webp, bmp and ico as accepted values. The documentation is careful here: png is always supported by browsers, while the others are optional, meaning a given browser may or may not be able to encode them. If you hard code webp for a JPEG pipeline, some users will hit a component that cannot produce the format you asked for.

Accessibility inputs are present as well. imageAltText sets alternative text for the uploaded image, and cropperFrameAriaLabel labels the focusable cropper frame, defaulting to the string 'Crop photo'. Both are optional inputs and both let a keyboard or screen reader user understand what the focusable rectangle in the image represents.

The Angular 17.3 floor and what it costs to stay on it

Version 9.0.0 was published on 2024-11-21 and the upgrade notes say that only Angular 17.3 and later are supported from that release onward. The workspace package.json pins that floor directly, with the core, forms, router and platform browser packages all declared at 17.3.12 and the build tooling at the matching devkit version. The library also lists hammerjs as a development dependency, which is a leftover from the era when Angular touch gestures required that extra library.

The practical consequence is an upgrade ceiling for anything that depends on this component. An application that has moved to a newer Angular major line cannot simply bump the cropper package, because the compatibility promise attached to 9.0.0 stops at 17.3. Check your own Angular version against that floor before you add the package, and budget for rechecking the component whenever you upgrade Angular itself.

The repository layout shows the library living under projects/ngx-image-cropper, with a demo application built separately, and the scripts section exposes a dedicated library build, a watch mode for it, and a packaging path that publishes the built library folder. That structure is what makes the published artefact separate from the demo, and it is worth knowing if you plan to contribute a patch rather than work around a limitation.

Pintura is the alternative the project itself points to

The README names one alternative rather than staying silent, and it is a commercial product rather than another Angular component. Pintura is described as covering cropping, rotating, flipping, filtering and annotating, across mobile and desktop devices and for both image and video editing. The project links to a live editor and to a comparison animation side by side with its own cropper.

The difference in approach is therefore a difference in scope. ngx-image-cropper does one thing, the crop rectangle, inside an Angular form, with output you upload yourself. A suite such as Pintura replaces the crop rectangle with a full editing surface that already contains rotation, filters and annotation, and that ships as a vendor product rather than an MIT library. If your product roadmap includes anything beyond framing a profile picture, the gap between the two is wide. The README does not state Pintura's licence or price, so the commercial terms have to be checked with the vendor directly.

If you want to stay inside Angular and add the missing capabilities, the honest answer is that this component offers no hook for rotation or filtering in its documented input list, so those features would come from your own code around the cropper.

The branch moved in 2026 while the last release stayed in 2024

The repository is not archived, and the last push was on 2026-10-12. The published releases tell a different story about the library's public surface: 9.0.0 on 2024-11-21, 8.1.0 on 2024-10-27 and 8.0.0 on 2024-05-16. So the branch receives commits while the released version has stood unchanged since November 2024. Recent work on master has not yet produced a new major or minor release.

That gap has two practical consequences. Code on master may differ from the version npm installs, so reading the source on the default branch is not the same as reading what your build will contain. And when you need a fix, the upgrade notes for breaking changes are the releases page, which currently ends at 9.0.0.

The licence is MIT, so there is no copyleft obligation on a proprietary application that embeds the component. The cost of an upgrade is mostly Angular coupling, as described earlier, plus the need to confirm that the release you are moving to still covers the Angular version your app runs.

Editorial conclusion

Adopt ngx-image-cropper if your Angular app needs a single, focused crop box tied to a file input, and you are willing to write the upload plumbing yourself. Skip it if you need rotation, filters or annotation, or if you want a component that does not constrain you to Angular 17.3 and up. Before you commit, open the cropper in the browsers you support and confirm whether the payload arrives as a blob or as a base64 string, since the two descriptions in the documentation do not match.

Frequently asked questions

How do I install ngx-image-cropper in an Angular project?

Run npm install ngx-image-cropper --save. The component is a standalone Angular component, so you import ImageCropperComponent into the component that renders it and add it to that component's imports array.

Which Angular versions does ngx-image-cropper support?

Only Angular 17.3 and later are supported since version 9.0.0. The workspace pins the Angular packages at 17.3.12, so an app on a newer major line has to check compatibility before upgrading this dependency.

What format does the imageCropped event deliver?

The documented output input accepts blob or base64 with blob as the default and the most performant option. The example code reads event.objectUrl for a preview and mentions event.blob for uploads, while one prose sentence describes the payload as a base64 string.

Why does an image loaded from a URL fail to appear in the cropper?

When the image comes from a different domain, Cross-Origin Resource Sharing must be allowed on that server or the image fails to load. The component reports the failure through the loadImageFailed event.

Can ngx-image-cropper rotate or filter an image?

The documented inputs cover loading, aspect ratio, output format and accessibility text, but no rotation or filtering. The README points to Pintura for cropping, rotating, flipping, filtering and annotating as an alternative.

Official sources

  1. Issues
  2. License: MIT
  3. Mawi137/ngx-image-cropper on GitHub
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mawi137-ngx-image-cropper.svg)](https://hysenlabs.com/projects/mawi137-ngx-image-cropper)

Was this useful?