Skip to main content

Recipes

Stencil Tuning#

Change the stencil#

There are only two default stencil components now, RectangleStencil (default) and CircleStencil, but you can easily create your own stencil himself.

To specify stencil component you should pass it to stencilComponent prop. For globally registered component just pass their name:

<Cropper    stencilComponent={CircleStencil}/>
import React, { useState } from 'react';import { CircleStencil, Cropper } from 'react-advanced-cropper';
export const Example = () => {    const [image] = useState(        'https://images.unsplash.com/photo-1599140849279-1014532882fe?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1300&q=80',    );
    return (        <Cropper            src={image}            stencilComponent={CircleStencil}        />    )};

Pass props to the stencil#

To pass any props to the stencil pass them as object to stencilProps prop.

For example, that's how you can set aspect ratio:

<Cropper    stencilProps={{        aspectRatio: 6/9,        movable: false,        resizable: false    }}/>

The list of available props varies from one stencil component to another. The props of the default stencils are available in the documentation (RectangleStencil, CircleStencil)

Set the aspect ratio#

This library supports setting either aspect ratio value or aspect ratio range (i.e. minimum and maximums aspect ratio values).

Generally speaking, aspect ratio is the property of the stencil, not the cropper, so the possibility to set aspect ratio entirely depends of used stencil. For example, CircleStencil aspect ratio can't be customized, it's always a circle, at the same time RectangleStencil has not any restrictions on aspect ratios.

The examples below are written for RectangleStencil.

Fixed aspect ratio#

<Cropper    stencilProps={{        aspectRatio: 1/1,    }}/>

Aspect ratio range#

<Cropper    stencilProps={{        aspectRatio: {            minimum: 16/8,            maximum: 4/8        }    }}/>

Add the grid#

You can enable the stencil grid for the default stencils to facilitate the selecting of the cropped area.

<Cropper    stencilProps={{        grid: true    }}/>

Getting the result#

First approach#

To get the current stencil coordinates and canvas with cropped image you can call the cropper methods getCoordinates and getCanvas respectively.

Click at the button Crop Image below to see this method in action

Crop Image
import React, { useState, useRef } from 'react';import { Cropper, CropperRef, Coordinates } from 'react-advanced-cropper';
export const Example = () => {    const cropperRef = useRef<CropperRef>(null);
    const [coordinates, setCoordinates] = useState<Coordinates | null>(null);    const [image, setImage] = useState<string>();
    const [src, setSrc] = useState(        'https://images.unsplash.com/photo-1599140849279-1014532882fe?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1300&q=80',    );
    const onCrop = () => {        if (cropperRef.current) {            setCoordinates(cropperRef.current.getCoordinates());            // You are able to do different manipulations at a canvas            // but there we just get a cropped image, that can be used            // as src for <img/> to preview result            setImage(cropperRef.current.getCanvas()?.toDataURL());        }    };
    return (        <Cropper            ref={cropperRef}            src={src}        />;    )};

Second approach#

Also there is alternative to get the cropper result. You can get the coordinates of stencil and canvas with cropped image by processing one of numerous callbacks.

The example below shows processing the onChange event.

tip

Though, be careful. Getting the canvas result in onChange callback can heavily affect the performance, it's better to use this approach to get the coordinates, transitions and other plain properties. If you still need to get the canvas result in this callback, use debounce.

import { Cropper, CropperRef } from 'react-advanced-cropper';
export const Example = () => {    const [coordinates, setCoordinates] = useState<Coordinates | null>(null);    const [image, setImage] = useState<string>();
    const [src] = useState(        'https://images.unsplash.com/photo-1599140849279-1014532882fe?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1300&q=80',    );
    const onChange = (cropper: CropperRef) => {        setCoordinates(cropper.getCoordinates());        setImage(cropper.getCanvas()?.toDataURL());    };
    return (        <Cropper            src={src}            onChange={onChange}        />;    )};

Resize the result#

If you use coordinates only, the result should be scaled on server-side, but if you use canvas you may prefer to resize the result on client-side.

The most simplest way to do it is pass the restrictions for the result size in getCanvas method options.

const canvas = cropper.getCanvas({        minHeight: 0,        minWidth: 0,        maxHeight: 2048,        maxWidth: 2048,})

If you need to set the specific height and width use height and width attributes. But you should note that canvas will have the same aspect ratio as the stencil, so the result size may be different than one that you have set.

const canvas = cropper.getCanvas({        height: 256,        width: 256,})
Max width
Min width
Width
Height
Max area (width × height)
Crop

It uses default canvas image scaling procedure under the hood. If the result doesn't suit you, try to use the external libraries to resize image (pica, downscale and etc.)

Preview the result#

To implement real-time preview of cropping result you can use CropperPreview component.


There are currently exists two competing approaches to use <CropperPreview/>. Which one will remain still not decided, so feel free to use any of them.

import React, { useRef, useState } from 'react';import {    Cropper,    CropperPreview,    CropperState,    CropperImage,    CropperTransitions,} from 'react-advanced-cropper';
interface PreviewState {    state: CropperState | null;    image: CropperImage | null;    transitions: CropperTransitions | null;    loading?: false;    loaded?: false;}
export const Example = () => {    const [previewState, setPreviewState] = useState<PreviewState>({        state: null,        image: null,        transitions: null    });
    const [src, setSrc] = useState(        'https://images.unsplash.com/photo-1623432532623-f8f1347d954c?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=387&q=80',    );
    const onUpdate = () => {        setPreviewState({            state: cropper.getState(),            image: cropper.getImage(),            transitions: cropper.getTransitions(),            loaded: cropper.isLoaded(),            loading: cropper.isLoading(),        });    };
    return (        <div>            <Cropper                ref={cropperRef}                className="cropper"                stencilProps={{ aspectRatio: 1 }}                src={src}                onUpdate={onUpdate}            />            <CropperPreview                ref={previewRef}                className="preview"                {...previewState}            />        </div>    );};

As an alternative you can add the preview component inside the cropper wrapper, just like the slider in this tutorial. It can even improve the performance slightly.

Load image from a disc#

The image loading doesn't depend at this library and can be completed by a numerous ways. There will be considered only one of them.

That's what you will get:

Warning!

Blob is used in the example below. Ensure that browsers that you support can fully handle it or use the corresponding polyfill.

import React, { ChangeEvent, useState, useRef, useEffect } from 'react';import { CropperRef, Cropper } from 'react-advanced-cropper';
interface Image {    type?: string;    src: string;}
export const UploadExample = () => {    const inputRef = useRef<HTMLInputElement>(null);
    const [image, setImage] = useState<Image | null>(null);
    const onUpload = () => {        if (inputRef.current) {            inputRef.current.click();        }    };
    const onLoadImage = (event: ChangeEvent<HTMLInputElement>) => {        // Reference to the DOM input element        const { files } = event.target;
        // Ensure that you have a file before attempting to read it        if (files && files[0]) {            // Create the blob link to the file to optimize performance:            const blob = URL.createObjectURL(files[0]);
            // Get the image type from the extension. It's the simplest way, though be careful it can lead to an incorrect result:            setImage({               src: blob,               type: files[0].type            })        }        // Clear the event target value to give the possibility to upload the same image:        event.target.value = '';    };
    useEffect(() => {        // Revoke the object URL, to allow the garbage collector to destroy the uploaded before file        return () => {            if (image && image.src) {                URL.revokeObjectURL(image.src);            }        };    }, [image]);
    return (        <div className="upload-example">            <Cropper className="upload-example__cropper" src={image && image.src} />            <div className="buttons-wrapper">                <button className="button" onClick={onUpload}>                    <input ref={inputRef} type="file" accept="image/*" onChange={onLoadImage} />                    Upload image                </button>            </div>        </div>    );};

Upload image to a server#

The preferred method to upload image to the server is using a blob (for IE-11 you should use polyfill). The detailed explanation why you shouldn't use the data-url you can read in the great answer on Stackoverflow.

There are different approaches to implement image uploading because it depends on your backend. The one of them is presented below.

Crop Image
tip

Look the network section in your developer tools to examine the sent request.

import React, { useState, useRef } from 'react';import { CropperRef, Cropper } from 'react-advanced-cropper';
export const UploadExample = () => {    const cropperRef = useRef<CropperRef>(null);
    const [image] = useState(        'https://images.unsplash.com/photo-1604335079441-274c03ad99a1?ixlib=rb-1.2.1&auto=format&fit=crop&w=1024&q=80',    );
    const onUpload = () => {        const canvas = cropperRef.current?.getCanvas();        if (canvas) {            const form = new FormData();            canvas.toBlob((blob) => {                if (blob) {                    form.append('file', blob);                    fetch('http://example.com/upload/', {                        method: 'POST',                        body: form,                    });                }            }, 'image/jpeg');        }    };
    return (        <Cropper            ref={cropperRef}            src={image}        />    );};