A responsive image cropping tool for React with no dependencies.
—
The ReactCrop component is the main interface for image cropping functionality. It's a controlled React component that requires the parent to manage crop state through callbacks.
The main React component providing complete cropping functionality with drag handles, touch support, and keyboard accessibility.
/**
* Main React component for image cropping functionality
* Supports touch interactions, keyboard navigation, and comprehensive customization
*/
class ReactCrop extends PureComponent<ReactCropProps, ReactCropState> {
static xOrds: string[];
static yOrds: string[];
static xyOrds: string[];
static nudgeStep: number;
static nudgeStepMedium: number;
static nudgeStepLarge: number;
}
interface ReactCropProps {
/** Current crop state - component is controlled via this prop */
crop?: Crop;
/** Required callback fired on every crop change */
onChange: (crop: PixelCrop, percentageCrop: PercentCrop) => void;
/** Elements to crop (typically img or video elements) */
children?: React.ReactNode;
/** Fixed aspect ratio for the crop (e.g., 1 for square, 16/9 for landscape) */
aspect?: number;
/** Show crop as circle instead of rectangle */
circularCrop?: boolean;
/** Disable all crop interactions */
disabled?: boolean;
/** Prevent resize/create but allow dragging existing crop */
locked?: boolean;
/** Minimum crop width in pixels */
minWidth?: number;
/** Minimum crop height in pixels */
minHeight?: number;
/** Maximum crop width in pixels */
maxWidth?: number;
/** Maximum crop height in pixels */
maxHeight?: number;
/** Callback fired when drag/resize operation completes */
onComplete?: (crop: PixelCrop, percentageCrop: PercentCrop) => void;
/** Callback fired when drag operation starts */
onDragStart?: (e: PointerEvent) => void;
/** Callback fired when drag operation ends */
onDragEnd?: (e: PointerEvent) => void;
/** Custom component rendered inside crop selection area */
renderSelectionAddon?: (state: ReactCropState) => React.ReactNode;
/** Show rule of thirds grid lines */
ruleOfThirds?: boolean;
/** Prevent crop deselection when clicking outside */
keepSelection?: boolean;
/** Accessibility labels for screen readers */
ariaLabels?: AriaLabels;
/** CSS classes to apply to component */
className?: string;
/** Inline styles for component */
style?: React.CSSProperties;
}
interface ReactCropState {
/** Whether crop is currently being manipulated */
cropIsActive: boolean;
/** Whether user is drawing a new crop selection */
newCropIsBeingDrawn: boolean;
}Basic Usage:
import React, { useState } from "react";
import ReactCrop, { Crop, PixelCrop, PercentCrop } from "react-image-crop";
function ImageCropper() {
const [crop, setCrop] = useState<Crop>();
return (
<ReactCrop
crop={crop}
onChange={(pixelCrop: PixelCrop, percentCrop: PercentCrop) => {
setCrop(pixelCrop);
}}
onComplete={(pixelCrop: PixelCrop, percentCrop: PercentCrop) => {
console.log('Crop completed:', pixelCrop);
}}
>
<img src="path/to/image.jpg" alt="Crop me" />
</ReactCrop>
);
}Advanced Usage with Fixed Aspect Ratio:
import React, { useState } from "react";
import ReactCrop, { Crop, PixelCrop, PercentCrop, makeAspectCrop, centerCrop } from "react-image-crop";
function SquareCropper() {
const [crop, setCrop] = useState<Crop>();
const onImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {
const { naturalWidth: width, naturalHeight: height } = e.currentTarget;
// Create a centered square crop
const newCrop = centerCrop(
makeAspectCrop({ unit: '%' }, 1, width, height),
width,
height
);
setCrop(newCrop);
};
return (
<ReactCrop
crop={crop}
aspect={1} // Square aspect ratio
onChange={(_, percentCrop) => setCrop(percentCrop)}
>
<img
src="path/to/image.jpg"
alt="Crop me"
onLoad={onImageLoad}
/>
</ReactCrop>
);
}Circular Crop Usage:
import React, { useState } from "react";
import ReactCrop, { Crop, PixelCrop, PercentCrop } from "react-image-crop";
function CircularCropper() {
const [crop, setCrop] = useState<Crop>({
unit: '%',
x: 25,
y: 25,
width: 50,
height: 50,
});
return (
<ReactCrop
crop={crop}
circularCrop={true}
aspect={1} // Square aspect for perfect circle
onChange={(_, percentCrop) => setCrop(percentCrop)}
>
<img src="path/to/image.jpg" alt="Crop me" />
</ReactCrop>
);
}Static properties providing constants for crop manipulation and keyboard interaction.
/**
* Static properties on ReactCrop class
*/
class ReactCrop {
/** X-axis drag handle ordinates: ['e', 'w'] */
static xOrds: string[];
/** Y-axis drag handle ordinates: ['n', 's'] */
static yOrds: string[];
/** Corner drag handle ordinates: ['nw', 'ne', 'se', 'sw'] */
static xyOrds: string[];
/** Default keyboard nudge distance: 1 pixel */
static nudgeStep: number;
/** Medium keyboard nudge distance (Shift key): 10 pixels */
static nudgeStepMedium: number;
/** Large keyboard nudge distance (Ctrl/Cmd key): 100 pixels */
static nudgeStepLarge: number;
}The component supports full keyboard navigation:
nudgeStep pixelsnudgeStepMedium pixelsnudgeStepLarge pixelsThe component automatically handles touch interactions:
The component includes default CSS classes for styling:
.ReactCrop: Main container.ReactCrop--active: Applied during crop manipulation.ReactCrop--disabled: Applied when disabled prop is true.ReactCrop--locked: Applied when locked prop is true.ReactCrop--circular-crop: Applied when circularCrop prop is true.ReactCrop--fixed-aspect: Applied when aspect prop is set.ReactCrop--rule-of-thirds: Applied when ruleOfThirds prop is trueImport the CSS file for default styling:
@import "react-image-crop/dist/ReactCrop.css";Or use the SCSS source for customization:
@import "react-image-crop/src/ReactCrop.scss";Install with Tessl CLI
npx tessl i tessl/npm-react-image-crop