Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Scale, Crop, and Zoom Images in a React Web Application

TwitterFacebookRedditLinkedInHacker News

If you’ve been keeping up with the blog, you might remember a few tutorials I wrote around the Cropper.js library for JavaScript applications. In these tutorials I demonstrated how to manipulate images in Angular as well as Vue.js web applications. While the tutorials were more or less the same, the framework was different, which resulted in some differences in the code used.

So what if we were building a React application and needed to manipulate images prior to uploading them to a server?

In this tutorial we’re going to see how to use Cropper.js to crop images within a React web application. While we won’t be uploading these images to a remote server for storage, such as a task can be easily accomplished with a bit of imagination.

To get an idea of what we want to accomplish, take a look at the following animated image:

Cropper.js in a React Application

As you can see there is an interactive canvas with a source image. The result of our manipulations are displayed in a “preview” box which can be saved if we wanted to. Realistically we’d send the result to a remote server, but that’s up to you.

Create a Simple React Application with Boilerplate Code

To keep things simple and easy to understand, we’re going to be working from a fresh project. From the command line, execute the following:

npx create-react-app image-crop-example

The above command will create a new project with the default template. The default template contains a little more code than we need, so let’s take a moment to clean it up.

Open the project’s src/App.js file and make it look like the following:

import React from 'react';

function App() {
    return (
        <div>
            <!-- custom component here -->
        </div>
    );
}

export default App;

In the above code we’ve essentially stripped out the text and images that the React CLI tool provides by default. At this point in time we’re ready to start adding our own custom code.

Developing an Image Manipulation React Component with Cropper.js Support

Like I had previously mentioned, we’re going to be using Cropper.js for all of the heavy lifting. To install it in our project, execute the following from the command line:

npm install cropperjs --save

We could start using this package in our src/App.js file, but it probably makes more sense to create a reusable component that we could easily drop in wherever we wanted.

Within the project, create a src/components/imagecropper.js file as well as a src/components/imagecropper.css file. For this example the custom CSS is less important to us, so we’ll get that out of the way first.

Open the project’s src/components/imagecropper.css file and include the following:

.img-container {
    width: 640px;
    height: 480px;
    float: left;
}

.img-preview {
    width: 200px;
    height: 200px;
    float: left;
    margin-left: 10px;
}

The above CSS places the source canvas and the destination preview next to each other like in the animated example. With the basic CSS out of the way, we can focus on the core material.

Open the project’s src/components/imagecropper.js file and include the following:

import React from "react";
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.min.css";
import "./imagecropper.css";

class ImageCropper extends React.Component {

    constructor() {
        super();
        this.state = {
            imageDestination: ""
        };
        this.imageElement = React.createRef();
    }

    componentDidMount() { }

    render() {
        return (
            <div>
                <div class="img-container">
                    <img ref={this.imageElement} src={this.props.src} alt="Source" crossorigin />
                </div>
                <img src={this.state.imageDestination} class="img-preview" alt="Destination" />
            </div>
        );
    }

}

export default ImageCropper;

The above code is not complete, but it is enough to get us started.

First you’ll notice that we’re importing the Cropper.js JavaScript and CSS. We’re also importing the custom CSS that we had defined for this particular component.

In the constructor method we’re defining our state variables, which in this case represents the final altered image. Because Cropper.js needs to interact with the HTML <img> component, we need to define a reference variable to contain it.

The render function brings the state and reference variables together:

render() {
    return (
        <div>
            <div class="img-container">
                <img ref={this.imageElement} src={this.props.src} alt="Source" crossorigin />
            </div>
            <img src={this.state.imageDestination} class="img-preview" alt="Destination" />
        </div>
    );
}

The goal here is to use the source image with Cropper.js. This source image is populated with properties defined by the user that’s using this particular component. We’ll get to that next. The destination image is using our state variable which we’ll define after the component mounts.

This brings us to the componentDidMount method:

componentDidMount() {
    const cropper = new Cropper(this.imageElement.current, {
        zoomable: false,
        scalable: false,
        aspectRatio: 1,
        crop: () => {
            const canvas = cropper.getCroppedCanvas();
            this.setState({ imageDestination: canvas.toDataURL("image/png") });
        }
    });
}

In this particular example, we’re only allowing cropping and moving. We’re also saying that the box must maintain a 1:1 aspect ratio. In other words any changes we make to our image must be a perfect square.

Take note of the crop function:

crop: () => {
    const canvas = cropper.getCroppedCanvas();
    this.setState({ imageDestination: canvas.toDataURL("image/png") });
}

When we crop, the canvas area is obtained and we’re storing it as image data in the imageDestination state variable. Changing this variable will cause it to immediately render again. You’ll see the data of this variable in the preview box.

If you had planned to send the altered image to a server, you’d probably want to do it in the crop function. There are quite a few options and functions, so if you’re looking for specific functionality, take a look at the official documentation for the package.

So let’s bring it together. We have a component, so now we need to use it.

Open the project’s src/App.js file and change it to the following:

import React from 'react';
import ImageCropper from "./components/imagecropper";

function App() {
    return (
        <div>
            <ImageCropper src="https://d33wubrfki0l68.cloudfront.net/446b1f54b7535dc5e58648c68222312c90c1aec6/14bd8/img/profile.jpg"></ImageCropper>
        </div>
    );
}

export default App;

Notice that we’ve now imported our ImageCropper component and are making use of it in the App function. The src property of the <ImageCropper> tag is a URL to an image that we wish to change.

Conclusion

You just saw how to use Cropper.js in a React application to scale, zoom, and crop images with UI functionality. This is typically a pre-processing step to allow your users to make changes to an image prior to uploading it to a server.

If you’re an Angular developer you can check out a variation of this tutorial in Image Cropping, Zooming, and Scaling with Angular and JavaScript. Likewise, I also have a Vue.js version if you’d prefer that as well.

A video version of this tutorial can be found below.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.

Search

Follow Us

The Polyglot Developer

Subscribe

Subscribe to the newsletter for monthly tips and tricks on subjects such as mobile, web, and game development.

The Polyglot Developer

Support This Site