Displaying images in CSS is always a tricky work, furthermore if the images have different sizes or aspect ratios. Some of the projects we are working on have the requirement to let the users upload their own pictures, so in order to avoid a CSS nightmare scenario we needed something to sanitize those images.

We could just crop the images automatically, but we wanted the user to choose the area and the zoom to be cropped, forcing the aspect-ratio. We wanted to avoid doing it from scratch, so for that purpose, we started searching for a library that could handle such work with little effort, something simple and effective.

Since we work with ClojureScript on our front-end, we first looked for a native library, but we had no luck. There was a library related to the topic, Clojure Image Resizer, but it wasn’t enough. It is for the back-end side, and our main goal was to provide a UI to choose how the image should be cropped.

As ClojureScript can also easily interop with JavaScript, we had a lot of libraries to choose from. The first two we found, Cropperjs and Croppie, are quite popular but we work in react based stack, therefore they weren’t very appropriate (JS objects manipulation, state…). It’s true that there is a React version of the first one but it is based on Cropper, and not in the new Cropperjs.

For React we found react-image-crop and react-easy-crop; we discarded the first one for the lack of zoom capabilities.

React Easy Crop had everything we needed to accomplish our task, in a very simple and easy way. It just had one drawback: it wasn’t available in CLJSJS Packages and we didn’t found a working CDN. The solution for this was quite straightforward - we just followed this guide to create our own CLJSJS package. Of course, we did a pull request so others can benefit from it (Already available in clojars)

Once we had the package required, this is the setup for a simple use of React Easy Crop.

And this is the result:

React Easy Crop doesn’t create the cropped image, instead it gives us a map with the selected area. That information is enough to generate the cropped image. We could delegate that work to our back-end (for example using Clojure Image Resizer), but we decided to do it in the front-end with vanilla JavaScript.

For that we came up with the following code:

As you can see the process-image function is multi-arity. With this we can choose if we want to force only the aspect-ratio or the size as well. The cropped-area map comes with the onCropComplete event. To add a listener we just need to add the :onCropComplete key to the cropper’s configuration map. For example:

:onCropComplete #(process-image @uploaded-image (js->clj %2 :keywordize-keys true))

In this example we are returning a base64 image, but you can change to a byte-array as well.

As a bonus, this is the code needed to handle the image upload:

And then of course, change the cropper’s image key value to use the atom.

This work was done pair programming with Lucas Sousa de Freitas