Home » Crop Image Before Upload in React JS

Crop Image Before Upload in React JS

react crop image before upload

In this post, we will learn how to crop an image before uploading in React JS. For cropping the image we will use a library called ‘react-easy-crop’ in this tutorial.

Video | React JS Crop Image Before Upload

As you can see in the above demo video when the user selects an image by clicking on Choose Image button. The selected image will be loaded into our image cropper. When the cropping is done we will display the cropped image.

Steps to Crop Image Before Uploading

  1. Installing react-easy-crop Package
  2. Creating File Input Component
  3. Create Image Cropper Component
  4. Using FileInput and ImageCropper Components

Let’s first create our react app using the create-react-app command.

npx create-react-app crop-image-reactjs

Installing react-easy-crop Package

Now, let’s install the react-easy-crop package using the following command.

npx install react-easy-crop

Creating File Input Component

After installing the library. Let’s create a component to choose an image from our local machine. We will create a file named FileInput.js.

FileInput.js

import React, { useRef } from "react";

function FileInput({ onImageSelected }) {
  const inputRef = useRef();

  const handleOnChange = (event) => {
    if (event.target.files && event.target.files.length > 0) {
      const reader = new FileReader();
      reader.readAsDataURL(event.target.files[0]);
      reader.onload = function (e) {
        onImageSelected(reader.result);
      };
    }
  };

  const onChooseImg = () => {
    inputRef.current.click();
  };

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        ref={inputRef}
        onChange={handleOnChange}
        style={{ display: "none" }}
      />

      <button className="btn" onClick={onChooseImg}>
        Choose Image
      </button>
    </div>
  );
}

export default FileInput;

Inside FileInput Component, we have an input element of type file which will aspect only images. And a button named Choose Image. We are using the useRef hook to handle the input element.

The handleOnChange() function will be invoked whenever the user chooses an image. In this function, we validate the file and store the selected image file path into a state variable.

Create Image Cropper Component

Now let’s create the actual image cropper component. For that create a component with the name ImageCropper.js.

ImageCropper.js

import React, { useState } from "react";
import Cropper from "react-easy-crop";

function ImageCropper({ image, onCropDone, onCropCancel }) {
  const [crop, setCrop] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [croppedArea, setCroppedArea] = useState(null);
  const [aspectRatio, setAspectRatio] = useState(4 / 3);

  const onCropComplete = (croppedAreaPercentage, croppedAreaPixels) => {
    setCroppedArea(croppedAreaPixels);
  };

  const onAspectRatioChange = (event) => {
    setAspectRatio(event.target.value);
  };

  return (
    <div className="cropper">
      <div>
        <Cropper
          image={image}
          aspect={aspectRatio}
          crop={crop}
          zoom={zoom}
          onCropChange={setCrop}
          onZoomChange={setZoom}
          onCropComplete={onCropComplete}
          style={{
            containerStyle: {
              width: "100%",
              height: "80%",
              backgroundColor: "#fff",
            },
          }}
        />
      </div>

      <div className="action-btns">
        <div className="aspect-ratios" onChange={onAspectRatioChange}>
          <input type="radio" value={1 / 1} name="ratio" /> 1:1
          <input type="radio" value={5 / 4} name="ratio" /> 5:4
          <input type="radio" value={4 / 3} name="ratio" /> 4:3
          <input type="radio" value={3 / 2} name="ratio" /> 3:2
          <input type="radio" value={5 / 3} name="ratio" /> 5:3
          <input type="radio" value={16 / 9} name="ratio" /> 16:9
          <input type="radio" value={3 / 1} name="ratio" /> 3:1
        </div>

        <button className="btn btn-outline" onClick={onCropCancel}>
          Cancel
        </button>

        <button
          className="btn"
          onClick={() => {
            onCropDone(croppedArea);
          }}
        >
          Done
        </button>
      </div>
    </div>
  );
}

export default ImageCropper;

In ImageCropper component, we have used the <Cropper /> component from react-easy-crop library. We are passing the image, aspect, crop ( x and y coordinated { x: number, y: number }), and zoom as props.

We also have the radio input elements to get the crop aspect ratio and the cancel button to discard the crop and the done button to finish the crop.

The onCropComplete() function will be triggered when the crop is completed and returns croppedAreaPercentage and croppedAreaPixels. We get the croppedAreaPixels and store it in the state variable.

The onAspectRatioChange() function is used to update the aspectRatio state variable when the user switched between different aspect ratios.

Using FileInput and ImageCropper Components

Finally, we will use the created component inside the App.js file. Here is the code for App.js

App.js

import React, { useState } from "react";
import FileInput from "./components/FileInput";
import ImageCropper from "./components/ImageCropper";

function App() {
  const [image, setImage] = useState("");
  const [currentPage, setCurrentPage] = useState("choose-img");
  const [imgAfterCrop, setImgAfterCrop] = useState("");

  // Invoked when new image file is selected
  const onImageSelected = (selectedImg) => {
    setImage(selectedImg);
    setCurrentPage("crop-img");
  };

  // Generating Cropped Image When Done Button Clicked
  const onCropDone = (imgCroppedArea) => {
    const canvasEle = document.createElement("canvas");
    canvasEle.width = imgCroppedArea.width;
    canvasEle.height = imgCroppedArea.height;

    const context = canvasEle.getContext("2d");

    let imageObj1 = new Image();
    imageObj1.src = image;
    imageObj1.onload = function () {
      context.drawImage(
        imageObj1,
        imgCroppedArea.x,
        imgCroppedArea.y,
        imgCroppedArea.width,
        imgCroppedArea.height,
        0,
        0,
        imgCroppedArea.width,
        imgCroppedArea.height
      );

      const dataURL = canvasEle.toDataURL("image/jpeg");

      setImgAfterCrop(dataURL);
      setCurrentPage("img-cropped");
    };
  };

  // Handle Cancel Button Click
  const onCropCancel = () => {
    setCurrentPage("choose-img");
    setImage("");
  };

  return (
    <div className="container">
      {currentPage === "choose-img" ? (
        <FileInput setImage={setImage} onImageSelected={onImageSelected} />
      ) : currentPage === "crop-img" ? (
        <ImageCropper
          image={image}
          onCropDone={onCropDone}
          onCropCancel={onCropCancel}
        />
      ) : (
        <div>
          <div>
            <img src={imgAfterCrop} className="cropped-img" />
          </div>

          <button
            onClick={() => {
              setCurrentPage("crop-img");
            }}
            className="btn"
          >
            Crop
          </button>

          <button
            onClick={() => {
              setCurrentPage("choose-img");
              setImage("");
            }}
            className="btn"
          >
            New Image
          </button>
        </div>
      )}
    </div>
  );
}

export default App;

In the above code, we have defined a few useState variables like ‘image’ to store the user-selected image, ‘currentPage’ to show and hide components based on its value, and ‘imgAfterCrop’ to store the cropped image.

And we have written three functions onImageSelected(), onCropDone(), and onCropCancel() inside our App.js component.

The onImageSelected() function is passed to the FileInput component as a prop. This function will be invoked when the user selected an image file.

Inside this function, we are updating the image state variable with the value which we get from FileInput Component, and we also update the currentPage state variable.

The onCropDone() and onCropCancel() functions is passed to the ImageCropper Component as a prop. This function will be called when the user clicks on the Done button.

This function will take croppedAreaPixels as a parameter and draws only the cropped image on the canvas. Then we use the toDataURL() method on canvas to get the image and store it in the ‘imgAfterCrop’ state variable.

Also Check out:

The onCropCancel() function, is invoked when the Cancel button is clicked. It will reset the image and currentPage state variables.

Output

image cropper in react js

Conclusion

In this tutorial, we have learned how to crop images before uploading in React JS. Hope it was helpful. Thanks