A React component to crop images/videos with easy interactions
npx @tessl/cli install tessl/npm-react-easy-crop@5.5.00
# React Easy Crop
1
2
React Easy Crop is a React component library that provides an intuitive interface for cropping images and videos with drag, zoom, and rotate interactions. It supports multiple media formats, offers mobile-friendly touch gestures, and provides crop coordinates in both pixels and percentages.
3
4
## Package Information
5
6
- **Package Name**: react-easy-crop
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install react-easy-crop`
10
11
## Core Imports
12
13
```typescript
14
import Cropper from "react-easy-crop";
15
import type { CropperProps, Area, Point, Size, MediaSize, VideoSrc } from "react-easy-crop";
16
```
17
18
For helper functions:
19
20
```typescript
21
import {
22
getInitialCropFromCroppedAreaPixels,
23
getInitialCropFromCroppedAreaPercentages
24
} from "react-easy-crop";
25
```
26
27
CommonJS:
28
29
```javascript
30
const Cropper = require("react-easy-crop").default;
31
const { getInitialCropFromCroppedAreaPixels, getInitialCropFromCroppedAreaPercentages } = require("react-easy-crop");
32
```
33
34
## Basic Usage
35
36
```typescript
37
import React, { useState, useCallback } from 'react';
38
import Cropper from 'react-easy-crop';
39
import type { Area, Point } from 'react-easy-crop';
40
41
const CropDemo = () => {
42
const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
43
const [zoom, setZoom] = useState(1);
44
const [rotation, setRotation] = useState(0);
45
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
46
47
const onCropComplete = useCallback(
48
(croppedArea: Area, croppedAreaPixels: Area) => {
49
setCroppedAreaPixels(croppedAreaPixels);
50
},
51
[]
52
);
53
54
return (
55
<div style={{ position: 'relative', width: '100%', height: 400 }}>
56
<Cropper
57
image="/path/to/image.jpg"
58
crop={crop}
59
zoom={zoom}
60
rotation={rotation}
61
aspect={4 / 3}
62
onCropChange={setCrop}
63
onZoomChange={setZoom}
64
onRotationChange={setRotation}
65
onCropComplete={onCropComplete}
66
style={{}}
67
classes={{}}
68
mediaProps={{}}
69
cropperProps={{}}
70
/>
71
</div>
72
);
73
};
74
```
75
76
## Capabilities
77
78
### Cropper Component
79
80
The main React component for image and video cropping with interactive controls.
81
82
```typescript { .api }
83
interface CropperProps {
84
/** Current crop position */
85
crop: Point;
86
/** Current zoom level */
87
zoom: number;
88
/** Current rotation in degrees */
89
rotation: number;
90
/** Crop aspect ratio */
91
aspect: number;
92
/** Crop position change callback */
93
onCropChange: (location: Point) => void;
94
95
/** Image URL or base64 string */
96
image?: string;
97
/** Video source(s) */
98
video?: string | VideoSrc[];
99
/** Custom CSS transform */
100
transform?: string;
101
/** Minimum zoom level */
102
minZoom: number;
103
/** Maximum zoom level */
104
maxZoom: number;
105
/** Shape of crop area */
106
cropShape: 'rect' | 'round';
107
/** Fixed crop size */
108
cropSize?: Size;
109
/** Media object fit (default: 'contain') */
110
objectFit?: 'contain' | 'cover' | 'horizontal-cover' | 'vertical-cover';
111
/** Show crop grid overlay */
112
showGrid?: boolean;
113
/** Zoom sensitivity */
114
zoomSpeed: number;
115
/** Enable zoom with mouse wheel */
116
zoomWithScroll?: boolean;
117
/** Round crop area pixel values */
118
roundCropAreaPixels?: boolean;
119
/** Restrict crop position within media bounds */
120
restrictPosition: boolean;
121
/** Keyboard navigation step size */
122
keyboardStep: number;
123
124
/** Zoom level change callback */
125
onZoomChange?: (zoom: number) => void;
126
/** Rotation change callback */
127
onRotationChange?: (rotation: number) => void;
128
/** Crop interaction complete callback */
129
onCropComplete?: (croppedArea: Area, croppedAreaPixels: Area) => void;
130
/** Crop area change callback */
131
onCropAreaChange?: (croppedArea: Area, croppedAreaPixels: Area) => void;
132
/** Crop size change callback */
133
onCropSizeChange?: (cropSize: Size) => void;
134
/** Interaction start callback */
135
onInteractionStart?: () => void;
136
/** Interaction end callback */
137
onInteractionEnd?: () => void;
138
/** Media loaded callback */
139
onMediaLoaded?: (mediaSize: MediaSize) => void;
140
141
/** Custom styles */
142
style: {
143
containerStyle?: React.CSSProperties;
144
mediaStyle?: React.CSSProperties;
145
cropAreaStyle?: React.CSSProperties;
146
};
147
/** Custom CSS classes */
148
classes: {
149
containerClassName?: string;
150
mediaClassName?: string;
151
cropAreaClassName?: string;
152
};
153
154
/** Props passed to media element */
155
mediaProps: React.ImgHTMLAttributes<HTMLElement> | React.VideoHTMLAttributes<HTMLElement>;
156
/** Props passed to cropper div */
157
cropperProps: React.HTMLAttributes<HTMLDivElement>;
158
/** Disable automatic CSS injection */
159
disableAutomaticStylesInjection?: boolean;
160
/** Initial crop area in pixels */
161
initialCroppedAreaPixels?: Area;
162
/** Initial crop area in percentages */
163
initialCroppedAreaPercentages?: Area;
164
/** Touch request handler */
165
onTouchRequest?: (e: React.TouchEvent<HTMLDivElement>) => boolean;
166
/** Wheel request handler */
167
onWheelRequest?: (e: WheelEvent) => boolean;
168
/** Cropper ref callback */
169
setCropperRef?: (ref: React.RefObject<HTMLDivElement>) => void;
170
/** Image ref callback */
171
setImageRef?: (ref: React.RefObject<HTMLImageElement>) => void;
172
/** Video ref callback */
173
setVideoRef?: (ref: React.RefObject<HTMLVideoElement>) => void;
174
/** Media size callback */
175
setMediaSize?: (size: MediaSize) => void;
176
/** Crop size callback */
177
setCropSize?: (size: Size) => void;
178
/** CSP nonce for injected styles */
179
nonce?: string;
180
}
181
182
declare const Cropper: React.ComponentType<CropperProps>;
183
export default Cropper;
184
```
185
186
### Helper Functions
187
188
Functions to compute initial crop settings from predefined cropped areas.
189
190
```typescript { .api }
191
/**
192
* Compute initial crop position and zoom from pixel coordinates
193
* @param croppedAreaPixels - The crop area in pixels
194
* @param mediaSize - The media dimensions
195
* @param rotation - Current rotation in degrees (default: 0)
196
* @param cropSize - The crop area size
197
* @param minZoom - Minimum zoom level
198
* @param maxZoom - Maximum zoom level
199
* @returns Initial crop position and zoom level
200
*/
201
function getInitialCropFromCroppedAreaPixels(
202
croppedAreaPixels: Area,
203
mediaSize: MediaSize,
204
rotation?: number,
205
cropSize: Size,
206
minZoom: number,
207
maxZoom: number
208
): { crop: Point; zoom: number };
209
210
/**
211
* Compute initial crop position and zoom from percentage coordinates
212
* @param croppedAreaPercentages - The crop area in percentages
213
* @param mediaSize - The media dimensions
214
* @param rotation - Current rotation in degrees (required parameter)
215
* @param cropSize - The crop area size
216
* @param minZoom - Minimum zoom level
217
* @param maxZoom - Maximum zoom level
218
* @returns Initial crop position and zoom level
219
*/
220
function getInitialCropFromCroppedAreaPercentages(
221
croppedAreaPercentages: Area,
222
mediaSize: MediaSize,
223
rotation: number,
224
cropSize: Size,
225
minZoom: number,
226
maxZoom: number
227
): { crop: Point; zoom: number };
228
```
229
230
## Types
231
232
```typescript { .api }
233
/** Size dimensions */
234
interface Size {
235
width: number;
236
height: number;
237
}
238
239
/** Media size with natural dimensions */
240
interface MediaSize {
241
width: number;
242
height: number;
243
naturalWidth: number;
244
naturalHeight: number;
245
}
246
247
/** Point coordinates */
248
interface Point {
249
x: number;
250
y: number;
251
}
252
253
/** Rectangular area definition */
254
interface Area {
255
width: number;
256
height: number;
257
x: number;
258
y: number;
259
}
260
261
/** Video source configuration */
262
interface VideoSrc {
263
src: string;
264
type?: string;
265
}
266
```
267
268
## Usage Examples
269
270
### Image Cropping with Custom Aspect Ratio
271
272
```typescript
273
import React, { useState } from 'react';
274
import Cropper from 'react-easy-crop';
275
import type { Area, Point } from 'react-easy-crop';
276
277
const ImageCropper = ({ imageSrc }: { imageSrc: string }) => {
278
const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
279
const [zoom, setZoom] = useState(1);
280
const [croppedArea, setCroppedArea] = useState<Area | null>(null);
281
282
return (
283
<div style={{ position: 'relative', width: '100%', height: 400 }}>
284
<Cropper
285
image={imageSrc}
286
crop={crop}
287
zoom={zoom}
288
aspect={16 / 9}
289
onCropChange={setCrop}
290
onZoomChange={setZoom}
291
onCropComplete={(_, croppedAreaPixels) => setCroppedArea(croppedAreaPixels)}
292
showGrid={true}
293
cropShape="rect"
294
style={{}}
295
classes={{}}
296
mediaProps={{}}
297
cropperProps={{}}
298
/>
299
</div>
300
);
301
};
302
```
303
304
### Video Cropping with Multiple Sources
305
306
```typescript
307
import React, { useState } from 'react';
308
import Cropper from 'react-easy-crop';
309
import type { VideoSrc, Point } from 'react-easy-crop';
310
311
const VideoCropper = () => {
312
const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
313
const [zoom, setZoom] = useState(1);
314
315
const videoSources: VideoSrc[] = [
316
{ src: '/video.mp4', type: 'video/mp4' },
317
{ src: '/video.webm', type: 'video/webm' }
318
];
319
320
return (
321
<div style={{ position: 'relative', width: '100%', height: 400 }}>
322
<Cropper
323
video={videoSources}
324
crop={crop}
325
zoom={zoom}
326
aspect={1}
327
onCropChange={setCrop}
328
onZoomChange={setZoom}
329
cropShape="round"
330
showGrid={false}
331
style={{}}
332
classes={{}}
333
mediaProps={{}}
334
cropperProps={{}}
335
/>
336
</div>
337
);
338
};
339
```
340
341
### Circular Crop with Custom Styling
342
343
```typescript
344
import React, { useState } from 'react';
345
import Cropper from 'react-easy-crop';
346
import type { Point } from 'react-easy-crop';
347
348
const CircularCropper = ({ imageSrc }: { imageSrc: string }) => {
349
const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
350
const [zoom, setZoom] = useState(1);
351
352
return (
353
<div style={{ position: 'relative', width: '100%', height: 400 }}>
354
<Cropper
355
image={imageSrc}
356
crop={crop}
357
zoom={zoom}
358
aspect={1}
359
cropShape="round"
360
onCropChange={setCrop}
361
onZoomChange={setZoom}
362
showGrid={false}
363
style={{
364
containerStyle: {
365
backgroundColor: '#333'
366
},
367
cropAreaStyle: {
368
border: '2px solid #fff'
369
}
370
}}
371
classes={{
372
containerClassName: 'custom-cropper-container'
373
}}
374
mediaProps={{}}
375
cropperProps={{}}
376
/>
377
</div>
378
);
379
};
380
```
381
382
### Using Initial Crop from Saved Data
383
384
```typescript
385
import React, { useState, useEffect } from 'react';
386
import Cropper, { getInitialCropFromCroppedAreaPixels } from 'react-easy-crop';
387
import type { Area, Point, MediaSize } from 'react-easy-crop';
388
389
const CropperWithInitialCrop = ({ imageSrc, savedCropPixels }: {
390
imageSrc: string;
391
savedCropPixels: Area;
392
}) => {
393
const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
394
const [zoom, setZoom] = useState(1);
395
const [mediaSize, setMediaSize] = useState<MediaSize | null>(null);
396
397
useEffect(() => {
398
if (mediaSize && savedCropPixels) {
399
const { crop: initialCrop, zoom: initialZoom } = getInitialCropFromCroppedAreaPixels(
400
savedCropPixels,
401
mediaSize,
402
0, // rotation
403
{ width: 300, height: 200 }, // cropSize
404
1, // minZoom
405
3 // maxZoom
406
);
407
setCrop(initialCrop);
408
setZoom(initialZoom);
409
}
410
}, [mediaSize, savedCropPixels]);
411
412
return (
413
<div style={{ position: 'relative', width: '100%', height: 400 }}>
414
<Cropper
415
image={imageSrc}
416
crop={crop}
417
zoom={zoom}
418
aspect={3 / 2}
419
onCropChange={setCrop}
420
onZoomChange={setZoom}
421
onMediaLoaded={setMediaSize}
422
cropSize={{ width: 300, height: 200 }}
423
style={{}}
424
classes={{}}
425
mediaProps={{}}
426
cropperProps={{}}
427
/>
428
</div>
429
);
430
};
431
```