React Image Cropping
Learn how to integrate image cropping into your React.js application
Image cropping is an essential feature for many applications that involve image uploads. It allows users to select and focus on specific parts of an image, ensuring the content is perfectly framed before being stored. Depending on your application’s requirements, you might want to enable users to crop images before uploading them to your image storage service. In this guide, we will explore how to seamlessly integrate image cropping into your React application.
We will be making use of react-image-crop
library for this purpose. The cropper component (ReactCrop
) takes in some props, we will be focusing on some necessary props given below,
crop - Initial crop state.
onChange - A callback that happens for every change of the crop (drag/resize).
onComplete - A callback that happens after a resize, drag, or nudge.
aspect - Aspect ratio of cropper
Both onChange
and onComplete
callback passes the current crop state object on trigger.
import ReactCrop from 'react-image-crop'
// ...
<ReactCrop
crop={{ x: 0, y: 0, unit: 'px', height: 100, width: 50 }}
onChange={(crop) => {}}
onComplete={(crop) => {}}
aspect={1}
>
<img ... />
</ReactCrop>
//...
The crop state object includes,
width - Width of crop image
height - Height of crop image
x, y - Cordinates of top-left corner of crop image from to that of original image
unit - Unit of crop length (eg:
%
,px
)
You can get some vague idea of change in crop state on drag from screenshots below,
The example below illustrated the dragging and resizing events using the Cropper component for fixed aspect ratio
1
.
Now that we have the crop state object, we possess all the necessary details to crop the image. Cropping can be done either client-side or server-side. Sometimes, it’s useful to attempt client-side cropping first and, if it fails, upload the image without cropping and handle it server-side. If you choose to crop server-side, the Sharp library offers an efficient solution. In this guide, we’ll focus on client-side cropping using the Canvas API. By utilizing the drawImage
method, we can draw the cropped image onto the canvas based on the crop state,
// src/utils.js
function setCanvasImage(image, canvas, crop) {
if (!crop || !canvas || !image) {
return;
}
const scaleX = image.naturalWidth / image.width;
const scaleY = image.naturalHeight / image.height;
const ctx = canvas.getContext('2d');
// refer https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
const pixelRatio = window.devicePixelRatio;
canvas.width = crop.width * pixelRatio * scaleX;
canvas.height = crop.height * pixelRatio * scaleY;
// refer https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setTransform
ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
ctx.imageSmoothingQuality = 'high';
// refer https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
ctx.drawImage(
image,
crop.x * scaleX,
crop.y * scaleY,
crop.width * scaleX,
crop.height * scaleY,
0,
0,
crop.width * scaleX,
crop.height * scaleY
);
}
Once we have cropped image drawn in canvas we can download cropped image using toBlob
method,
// src/utils.js
function generateDownload(canvas, crop) {
if (!crop || !canvas) {
return;
}
canvas.toBlob(
(blob) => {
const previewUrl = window.URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.download = 'cropPreview.png';
anchor.href = URL.createObjectURL(blob);
anchor.click();
window.URL.revokeObjectURL(previewUrl);
},
'image/png',
1
);
}
Next, we'll explore how to integrate Cropper into your React application. First, prompt the user to select an image file from their computer using an input element. Once an image is selected, load it into the Cropper component. Allow the user to define the crop area by dragging or resizing the selection. When the onCompleteCrop
event is triggered, draw the cropped image onto a canvas using the crop state. Finally, you can either let the user download the cropped image or upload it to your image storage service.
// src/App.jsx
import React, { useRef, useState } from 'react';
import ReactCrop from 'react-image-crop';
import { drawImageOnCanvas, generateDownload } from './utils';
import 'react-image-crop/dist/ReactCrop.css';
export default function App() {
const [imgSrc, setImgSrc] = useState();
const [crop, setCrop] = useState();
const [completedCrop, setCompletedCrop] = useState(null);
const imgRef = useRef(null);
const canvasRef = useRef(null);
// load the selected image on to cropeer
const handleFileSelect = (e) => {
if (e.target.files && e.target.files.length > 0) {
const reader = new FileReader();
reader.addEventListener('load', () => setImgSrc(reader.result));
reader.readAsDataURL(e.target.files[0]);
}
};
// on crop selection, draw it on canvas
const handleCompleteCrop = (crop) => {
drawImageOnCanvas(imgRef.current, canvasRef.current, crop);
setCompletedCrop(crop);
};
// download the cropped image drawn on canvas
const handleDownload = () => {
generateDownload(canvasRef.current, completedCrop);
};
// adjust canvas onto crop dimenstion
const canvasStyles = {
width: Math.round(completedCrop?.width ?? 0),
height: Math.round(completedCrop?.height ?? 0),
};
return (
<div className='App'>
<div className='FileSelector'>
<input
type='file'
accept='image/*'
onChange={handleFileSelect}
/>
</div>
<div className='CropperWrapper'>
<ReactCrop
crop={crop}
onChange={setCrop}
aspect={1}
onComplete={handleCompleteCrop}
>
{imgSrc && <img ref={imgRef} src={imgSrc} alt='cropper image' />}
</ReactCrop>
<div className='CanvasWrapper'>
<canvas ref={canvasRef} style={canvasStyles} />
</div>
</div>
<button
type='button'
disabled={!completedCrop}
onClick={handleDownload}
>
Download cropped image
</button>
</div>
);
}
Integrating image cropping into your React application enhances user experience by allowing precise image adjustments before upload. By leveraging the Cropper component, users can easily select and crop their images directly in the browser. With the crop state managed and the cropped image drawn onto a canvas, you have the flexibility to either provide the cropped image for download or upload it to your backend server. This approach ensures that images are perfectly framed and tailored to your application's needs, making the process seamless and efficient for users.
You can access the full source code for crop implementation in react - aseerkt/react-image-crop-demo.
Resources
Hope you were able to integrate react cropper feature onto your application. See you soon with some new content. Stay tuned.