How To Crop an Image in React

In this article, you will learn to crop an image in JavaScript. Specifically, you will see how to achieve this goal with the react-image-crop React library.


6 min read
How To Crop an Image in React

In this article, you will learn to crop an image in JavaScript. Specifically, you will see how to achieve this goal with the react-image-crop React library.

Over the last few years, providing users with features to deal with images has become more and more common. This is why every developer should know how to deal with basic operations on images, such as cropping. This gives them the possibility to choose only the areas they are interested in.

So, let’s see how to crop an image in React with react-image-crop. Follow this step-by-step tutorial to achieve the following result:

Prerequisites

This is the list of all the prerequisites for the demo application you are going to build:

Cropping an Image with react-image-crop

You can clone the GitHub repository that supports this article and try the demo application by launching the following commands:

git clone https://github.com/imgly/Blog-How-To-Crop-an-Image-in-React-with-React-Crop-Image.git
cd Blog-How-To-Crop-an-Image-in-React-with-React-Crop-Image
npm i
npm start

Otherwise, you can continue following this tutorial and build the demo application step by step.

1. Creating a React Project

The easiest way to create an empty working project in React is by using Create React App, the officially supported way to create single-page React applications. You can create a new project called react-image-cropper-demo with the following command:

npx create-react-app react-image-cropper-demo

You will now have a demo project located in the react-image-cropper-demo folder with this file structure:

react-image-cropper-demo
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── reportWebVitals.js
    └── setupTests.js

Move into the react-image-cropper-demo folder and launch a local server by running:

cd react-image-cropper-demo
npm start

Visit http://localhost:3000/ in your browser, and you should be able to see the default Create React App screen.

The default Create React App screen

2. Installing react-image-crop

Add the react-image-crop library to your project’s dependencies by running the following command:

npm install --save react-image-crop

Your package.json file will be updated accordingly, and you should now be able to see react-image-crop as a dependency.
Now, you have everything required to start developing your image cropper component.

3. Building the Image Cropper Component

First of all, create a components folder inside src. Then, make an ImageCropper folder containing index.js and index.css. These two files will contain the cropper component definition and style respectively.

The components folder

Initialize index.js with the following lines of code:

import React from "react";

function ImageCropper() {
    return (
        <div>
            {/*TODO*/}
        </div>
    );
}

export default ImageCropper;

This way, you have just created an empty ImageCropper component.
Now, you need to import ReactCrop, which is part of the react-image-crop library. Add it to the ImageCropper imports:

import ReactCrop from 'react-image-crop'

Plus, do not forget to import dist/ReactCrop.css or ReactCrop.scss as follows:

import 'react-image-crop/dist/ReactCrop.css';
// or scss:
import 'react-image-crop/lib/ReactCrop.scss';

This is what the final ImageCropper will look like:

import React, {useState} from "react";
import ReactCrop from 'react-image-crop';
import 'react-image-crop/dist/ReactCrop.css';
import demoImage from "./demo-image.jpg";

function ImageCropper(props) {
    const {imageToCrop, onImageCropped} = props;

    const [cropConfig, setCropConfig] = useState(
        // default crop config
        {
            unit: '%',
            width: 30,
            aspect: 16 / 9,
        }
    );

    const [imageRef, setImageRef] = useState();

    async function cropImage(crop) {
        if (imageRef && crop.width && crop.height) {
            const croppedImage = await getCroppedImage(
                imageRef,
                crop,
                'croppedImage.jpeg' // destination filename
            );

            // calling the props function to expose
            // croppedImage to the parent component
            onImageCropped(croppedImage);
        }
    }

    function getCroppedImage(sourceImage, cropConfig, fileName) {
        // creating the cropped image from the source image
        const canvas = document.createElement('canvas');
        const scaleX = sourceImage.naturalWidth / sourceImage.width;
        const scaleY = sourceImage.naturalHeight / sourceImage.height;
        canvas.width = cropConfig.width;
        canvas.height = cropConfig.height;
        const ctx = canvas.getContext('2d');

        ctx.drawImage(
            sourceImage,
            cropConfig.x * scaleX,
            cropConfig.y * scaleY,
            cropConfig.width * scaleX,
            cropConfig.height * scaleY,
            0,
            0,
            cropConfig.width,
            cropConfig.height
        );

        return new Promise((resolve, reject) => {
            canvas.toBlob(
                (blob) => {
                    // returning an error
                    if (!blob) {
                        reject(new Error('Canvas is empty'));
                        return;
                    }

                    blob.name = fileName;
                    // creating a Object URL representing the Blob object given
                    const croppedImageUrl = window.URL.createObjectURL(blob);

                    resolve(croppedImageUrl);
                }, 'image/jpeg'
            );
        });
    }

    return (
        <ReactCrop
            src={imageToCrop || demoImage}
            crop={cropConfig}
            ruleOfThirds
            onImageLoaded={(imageRef) => setImageRef(imageRef)}
            onComplete={(cropConfig) => cropImage(cropConfig)}
            onChange={(cropConfig) => setCropConfig(cropConfig)}
            crossorigin="anonymous" // to avoid CORS-related problems
        />
    );
}

ImageCropper.defaultProps = {
    onImageCropped: () => {}
}

export default ImageCropper;

imageToCrop is the source image received from the props. The or statement used when assigning the ReactCrop's src props assures that either imageToCrop or a default demo image is shown. After being loaded, a reference to the image is saved and then used when performing the cropping operation. Then, whenever a user uses the component to try to crop an image, the cropConfig object containing the crop settings is updated accordingly. Finally, when the user stops selecting the area to crop, cropImage is called. This function is in charge of producing the cropped image and passing it to the onImageCropped function received from the props.

4. Putting It All Together

Now it is time to see the ImageCropper component in action. All you need to do, is change the App.js file as follows:

import React, {useState} from "react";
import './App.css';
import ImageCropper from "./components/ImageCropper";

function App() {
    const [imageToCrop, setImageToCrop] = useState(undefined);
    const [croppedImage, setCroppedImage] = useState(undefined);

    const onUploadFile = (event) => {
        if (event.target.files && event.target.files.length > 0) {
            const reader = new FileReader();

            reader.addEventListener('load', () =>
                setImageToCrop(reader.result)
            );

            reader.readAsDataURL(event.target.files[0]);
        }
    };

    return (
        <div className="app">
            <input
                type="file"
                accept="image/*"
                onChange={onUploadFile}
            />
            <div>
                <ImageCropper
                    imageToCrop={imageToCrop}
                    onImageCropped={(croppedImage) => setCroppedImage(croppedImage)}
                />
            </div>
            {
                croppedImage &&
                <div>
                    <h2>Cropped Image</h2>
                    <img
                        alt="Cropped Image"
                        src={croppedImage}
                    />
                </div>
            }
        </div>
    );
}

export default App;

The input element allows users to upload an image, then stored it in imageToCrop and passed to ImageCropper. Each time a user crops it, the resulting image is saved thanks to the setCroppedImage function. Then, it is finally displayed in the Cropped Image section as presented in the fiddle at the beginning of the article.

Final Considerations on react-image-crop

Cropping an image from scratch is not an easy task. This is why using a library like react-image-crop is the recommended approach. As you have just seen, you can achieve your goal with just a few lines of code. This is great! On the other hand, these libraries are designed to achieve specific goals, like cropping an image. What if you need to perform or get users the possibility to do other image-related operations? You may be ending up with as many libraries as operations required.

Not only might they have very different UIs, but it may also be complicated to integrate them all in the same project. This is why in such a circumstance, a commercial and wider solution like PhotoEditorSDK should be the preferred approach. In fact, with only one library you get several tools to treat images as you want, while preserving the consistency of your application's UI. Also, whenever you need help, you can ask for support from the img.ly developers who built the SDK or visit one of the myriad snappy integration guides written by the developers on the blog.

Cropping an Image with PhotoEditorSDK

First, you should read this article from the official documentation on how to get started with PhotoEditorSDK in React. Then, by using the transform tool you can perform cropping, resizing, flipping, and rotation operations with just one feature. This way, you should be able to achieve the desired result:

crop-image-react

Conclusion

In this article, we looked at how to crop an image in React. Cropping an image can turn into a complex task and this is using a library should be the preferred approach. In particular, react-image-crop allows you to crop an image easily and with only a handful of lines of code, as we have seen. On the other hand, it is a library with a very specific purpose. So, if you needed to perform more than one operation on your images, you might need a more advanced and complete solution like PhotoEditorSDK.

Thanks for reading! We hope that you found this article helpful. Feel free to reach out to us on Twitter with any questions, comments, or suggestions.

Related Articles

CE.SDK v1.19 Release Notes
3 min read
CE.SDK v1.17 Release Notes
2 min read
CE.SDK v1.16 Release Notes
1 min read
CE.SDK v1.14 Release Notes
2 min read

GO TOP