react-cropper + mui with rotate and flip option
Published: 2022-03-13 10:14:18

Demo & Code : [https://codesandbox.io/s/react-cropper-js-mui-with-rotate-and-flip-option-y7mm6n](https://codesandbox.io/s/react-cropper-js-mui-with-rotate-and-flip-option-y7mm6n)

Download code from : [https://github.com/krishnapankhania/react-mui-cropper-demo](https://github.com/krishnapankhania/react-mui-cropper-demo)


![Cropper Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ce4lfpbs8wopq2hu271.gif)

## How to use?

Create react project. More stuff on [react docs](https://reactjs.org/docs/create-a-new-react-app.html).

```
npx create-react-app my-app
cd my-app
npm start
```

Let's install Material Ui react components, because we are going to use that

[https://mui.com](https://mui.com)

Run below command
```
npm install @mui/material @emotion/react @emotion/styled
```

Let's install react cropper

Run below command
```
npm i react-cropper
```
___

We will create 3 components

- Upload: File input. 
- Popup: A popup to open after the file is selected
- CropperDemo: Cropper logic resides here 

**Upload.js**

This component will take a file input from the user and will return file data to the parent component.

```js
import * as React from "react";
import { styled } from "@mui/material/styles";
import Button from "@mui/material/Button";

const Input = styled("input")({
  display: "none"
});

export default function Upload({ getUploadedFile }) {
  const onChange = (e) => {
    e.preventDefault();
    let files;
    if (e.dataTransfer) {
      files = e.dataTransfer.files;
    } else if (e.target) {
      files = e.target.files;
    }
    if (files.length === 0) {
      return alert("Please select a file.");
    }
    const reader = new FileReader();
    reader.onload = () => {
      getUploadedFile(reader.result);
    };
    reader.readAsDataURL(files[0]);
  };
  return (
    <label htmlFor="contained-button-file">
      <Input
        accept="image/*"
        id="contained-button-file"
        multiple
        type="file"
        onChange={onChange}
      />
      <Button variant="contained" component="span">
        Upload
      </Button>
    </label>
  );
}
```

**Popup.js**

This component is a MUI dialog component that will hold the cropper.

```js
import * as React from "react";
import Dialog from "@mui/material/Dialog";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import CropperDemo from "./Cropper";

export default function Popup({ open, image, handleClose, getCroppedFile }) {
  return (
    <div>
      <Dialog
        open={open}
        onClose={handleClose}
        aria-labelledby="alert-dialog-title"
        aria-describedby="alert-dialog-description"
      >
        <DialogTitle id="alert-dialog-title">Crop Image</DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            <CropperDemo
              handleClose={handleClose}
              src={image}
              getCroppedFile={getCroppedFile}
            />
          </DialogContentText>
        </DialogContent>
      </Dialog>
    </div>
  );
}
```
**Cropper.js**

This component does the cropping of an image and returns it to parent. Check the rotate and flip options as well. you can play around with other options too.

Check out available options 
- [https://fengyuanchen.github.io/cropperjs/](https://fengyuanchen.github.io/cropperjs/)
- [https://github.com/fengyuanchen/cropperjs](https://github.com/fengyuanchen/cropperjs)


```js
import React, { useRef, useState } from "react";
import Cropper from "react-cropper";
import Skeleton from "@mui/material/Skeleton";
import Button from "@mui/material/Button";
import ButtonGroup from "@mui/material/ButtonGroup";
import Box from "@mui/material/Box";

import "cropperjs/dist/cropper.css";

export default function CropperDemo({ src, getCroppedFile }) {
  const cropperRef = useRef(null);
  const [loading, setLoading] = useState(true);
  const [scaleX, setScaleX] = useState(1);
  const [scaleY, setScaleY] = useState(1);

  const handleClick = () => {
    const imageElement = cropperRef?.current;
    const cropper = imageElement?.cropper;
    const img = cropper.getCroppedCanvas().toDataURL();
    getCroppedFile(img);
  };
  const rotate = () => {
    const imageElement = cropperRef?.current;
    const cropper = imageElement?.cropper;
    cropper.rotate(90);
  };
  const flip = (type) => {
    const imageElement = cropperRef?.current;
    const cropper = imageElement?.cropper;
    if (type === "h") {
      cropper.scaleX(scaleX === 1 ? -1 : 1);
      setScaleX(scaleX === 1 ? -1 : 1);
    } else {
      cropper.scaleY(scaleY === 1 ? -1 : 1);
      setScaleY(scaleY === 1 ? -1 : 1);
    }
  };
  return (
    <>
      {loading && (
        <Skeleton variant="rectangular" width={"100%"} height={400} />
      )}
      <Box display={"flex"} justifyContent={"flex-end"} mb={1}>
        <ButtonGroup disableElevation variant="contained">
          <Button onClick={rotate}>Rotate</Button>
          <Button onClick={() => flip("h")}>Flip H</Button>
          <Button onClick={() => flip("v")}>Flip V</Button>
        </ButtonGroup>
      </Box>

      <Cropper
        src={src}
        style={{ height: 400, width: "100%" }}
        // Cropper.js options
        initialAspectRatio={16 / 9}
        guides={false}
        ready={() => {
          setLoading(false);
        }}
        ref={cropperRef}
      />
      <Button
        sx={{
          float: "right",
          mt: 1
        }}
        onClick={handleClick}
        autoFocus
        color="success"
        variant="contained"
      >
        Crop
      </Button>
    </>
  );
}
```

**App.js**

Let's use all components.

```js
import * as React from "react";
import Box from "@mui/material/Box";
import Upload from "./Upload";
import Popup from "./Popup";
export default function App() {
  const [open, setOpen] = React.useState(false);
  const [image, setImage] = React.useState(
    "https://images.unsplash.com/photo-1612232134966-a9b076b9fbe7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80"
  );

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <div>
      <Box
        sx={{
          display: "flex",
          flexDirection: "column",
          alignItems: "center"
        }}
      >
        <Box my={2}>
          <img src={image} alt="cropped" height={400} />
        </Box>

        <Upload
          getUploadedFile={(image) => {
            setOpen(true);
            setImage(image);
          }}
        />
        <Popup
          open={open}
          handleClose={handleClose}
          image={image}
          getCroppedFile={(image) => {
            setImage(image);
            handleClose();
          }}
        />
      </Box>
    </div>
  );
}
```