An image cropper for Ant Design Upload component that integrates react-easy-crop functionality with modal-based cropping interface
npx @tessl/cli install tessl/npm-antd-img-crop@4.25.0Antd Img Crop is a React component that integrates image cropping functionality with Ant Design's Upload component. It wraps the react-easy-crop library to provide a modal-based cropping interface with configurable options including aspect ratio control, zoom functionality, rotation capabilities, and quality settings.
npm install antd-img-cropimport ImgCrop from "antd-img-crop";
import type { ImgCropProps } from "antd-img-crop";For CommonJS:
const ImgCrop = require("antd-img-crop");For accessing the forwarded ref type (from react-easy-crop):
import type { CropperRef } from "react-easy-crop";For accessing the RcFile type (from Ant Design Upload):
import type { RcFile } from "antd/es/upload/interface";import { Upload } from "antd";
import ImgCrop from "antd-img-crop";
const Demo = () => (
<ImgCrop>
<Upload>+ Add image</Upload>
</ImgCrop>
);Advanced usage with configuration:
import { Upload } from "antd";
import ImgCrop from "antd-img-crop";
const Demo = () => (
<ImgCrop
aspect={16 / 9}
quality={0.8}
rotationSlider
showReset
modalTitle="Crop your image"
onModalOk={(file) => console.log("Cropped:", file)}
>
<Upload
maxCount={1}
beforeUpload={() => false}
>
+ Add image
</Upload>
</ImgCrop>
);The main component that wraps Ant Design Upload components to provide cropping functionality.
/**
* Main image cropping wrapper component that integrates with Ant Design Upload
* @param props - Configuration options for the cropping interface
* @param ref - Optional ref to access react-easy-crop instance
* @returns JSX element containing the wrapped Upload component
*
* Note: Component automatically detects browser language (zh-CN vs others)
* for default modal titles and button text when not explicitly provided
*/
const ImgCrop: React.ForwardRefExoticComponent<
ImgCropProps & React.RefAttributes<CropperRef>
>;
interface ImgCropProps {
/** Image quality for cropped output (0-1), default: 0.4 */
quality?: number;
/** Fill color for cropped image background, default: 'white' */
fillColor?: string;
/** Enable zoom slider control, default: true */
zoomSlider?: boolean;
/** Enable rotation slider control, default: false */
rotationSlider?: boolean;
/** Enable aspect ratio slider control, default: false */
aspectSlider?: boolean;
/** Show reset button for zoom/rotation/aspect, default: false */
showReset?: boolean;
/** Custom text for reset button */
resetText?: string;
/** Aspect ratio of crop area (width/height), default: 1 */
aspect?: number;
/** Minimum zoom level, default: 1 */
minZoom?: number;
/** Maximum zoom level, default: 3 */
maxZoom?: number;
/** Minimum aspect ratio, default: 0.5 */
minAspect?: number;
/** Maximum aspect ratio, default: 2 */
maxAspect?: number;
/** Shape of crop area: 'rect' or 'round', default: 'rect' */
cropShape?: 'rect' | 'round';
/** Show grid overlay in crop area, default: false */
showGrid?: boolean;
/** Additional props for react-easy-crop component */
cropperProps?: CropperProps;
/** CSS class name for the modal */
modalClassName?: string;
/** Title text for the crop modal */
modalTitle?: string;
/** Width of the crop modal */
modalWidth?: number | string;
/** Text for OK button */
modalOk?: string;
/** Text for Cancel button */
modalCancel?: string;
/** Callback when OK button is clicked */
onModalOk?: (value: BeforeUploadReturnType) => void;
/** Callback when Cancel button is clicked or modal is closed */
onModalCancel?: (resolve: (value: BeforeUploadReturnType) => void) => void;
/** Additional props for Ant Design Modal component */
modalProps?: ModalProps;
/** Callback before crop modal opens, can prevent modal from opening */
beforeCrop?: BeforeUpload;
/** Upload component to wrap (required) */
children: JSX.Element;
}
/** Ref type from react-easy-crop library providing access to cropper instance */
type CropperRef = import("react-easy-crop").CropperRef;/** Function type for beforeUpload and beforeCrop callbacks (derived from Ant Design Upload) */
type BeforeUpload = (
file: RcFile,
fileList: RcFile[]
) => boolean | Promise<boolean | File | Blob>;
/** Return type for upload-related callbacks */
type BeforeUploadReturnType = boolean | File | Blob | Promise<boolean | File | Blob>;
/** Props from react-easy-crop (heavily restricted subset available for configuration) */
interface CropperProps {
/** Keyboard step size for crop adjustment */
keyboardStep?: number;
/**
* Note: Most react-easy-crop props are excluded as they're controlled internally by ImgCrop.
* Excluded props include: image, crop, zoom, rotation, aspect, minZoom, maxZoom, minAspect,
* maxAspect, zoomWithScroll, cropShape, showGrid, onCropChange, onZoomChange,
* onRotationChange, onCropComplete, classes, and others.
*/
}
/** Props from Ant Design Modal (restricted subset available for configuration) */
interface ModalProps {
/**
* Additional modal configuration options excluding controlled properties.
* Excluded props: className, title, width, okText, cancelText, onOk, onCancel,
* open, visible, wrapClassName, maskClosable, destroyOnClose
*/
[key: string]: any;
}
/** File type from Ant Design Upload */
interface RcFile extends File {
uid: string;
/** Additional upload file properties */
[key: string]: any;
}
/** Internal constants that affect component behavior */
const ZOOM_STEP = 0.1; // Step size for zoom slider adjustments
const ROTATION_STEP = 1; // Step size for rotation slider adjustments (degrees)
const ROTATION_MIN = -180; // Minimum rotation value (degrees)
const ROTATION_MAX = 180; // Maximum rotation value (degrees)
const ASPECT_STEP = 0.01; // Step size for aspect ratio slider adjustmentsThe component handles various error scenarios:
accept prop (defaults to "image/*")beforeUpload callbackonModalCancel callback if provided, otherwise resolves with Upload.LIST_IGNOREbeforeCrop returns false or throws, the modal won't open and processing continues with original upload flowimport { Upload } from "antd";
import ImgCrop from "antd-img-crop";
const BasicCrop = () => (
<ImgCrop>
<Upload
maxCount={1}
beforeUpload={(file) => {
console.log("Final file:", file);
return false; // Prevent actual upload for demo
}}
>
Click to upload image
</Upload>
</ImgCrop>
);import { Upload } from "antd";
import ImgCrop from "antd-img-crop";
const CircularCrop = () => (
<ImgCrop
cropShape="round"
aspect={1}
rotationSlider
showReset
resetText="Reset Image"
>
<Upload maxCount={1}>
Upload Avatar
</Upload>
</ImgCrop>
);import { Upload } from "antd";
import ImgCrop from "antd-img-crop";
const CustomCrop = () => (
<ImgCrop
aspect={16 / 9}
quality={0.9}
aspectSlider
minAspect={1}
maxAspect={3}
modalTitle="Crop Banner Image"
modalWidth={800}
>
<Upload>
Upload Banner
</Upload>
</ImgCrop>
);import { Upload, message } from "antd";
import ImgCrop from "antd-img-crop";
const AdvancedCrop = () => (
<ImgCrop
beforeCrop={(file) => {
// Validate file before showing crop modal
if (file.size > 5 * 1024 * 1024) {
message.error("File too large!");
return false;
}
return true;
}}
onModalOk={(file) => {
console.log("Cropped file ready:", file);
}}
onModalCancel={(resolve) => {
console.log("Crop cancelled");
resolve(Upload.LIST_IGNORE);
}}
>
<Upload>
Upload and Crop
</Upload>
</ImgCrop>
);