or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-react-easy-crop

A React component to crop images/videos with easy interactions

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/react-easy-crop@5.5.x

To install, run

npx @tessl/cli install tessl/npm-react-easy-crop@5.5.0

index.mddocs/

React Easy Crop

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.

Package Information

  • Package Name: react-easy-crop
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install react-easy-crop

Core Imports

import Cropper from "react-easy-crop";
import type { CropperProps, Area, Point, Size, MediaSize, VideoSrc } from "react-easy-crop";

For helper functions:

import { 
  getInitialCropFromCroppedAreaPixels,
  getInitialCropFromCroppedAreaPercentages
} from "react-easy-crop";

CommonJS:

const Cropper = require("react-easy-crop").default;
const { getInitialCropFromCroppedAreaPixels, getInitialCropFromCroppedAreaPercentages } = require("react-easy-crop");

Basic Usage

import React, { useState, useCallback } from 'react';
import Cropper from 'react-easy-crop';
import type { Area, Point } from 'react-easy-crop';

const CropDemo = () => {
  const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [rotation, setRotation] = useState(0);
  const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);

  const onCropComplete = useCallback(
    (croppedArea: Area, croppedAreaPixels: Area) => {
      setCroppedAreaPixels(croppedAreaPixels);
    },
    []
  );

  return (
    <div style={{ position: 'relative', width: '100%', height: 400 }}>
      <Cropper
        image="/path/to/image.jpg"
        crop={crop}
        zoom={zoom}
        rotation={rotation}
        aspect={4 / 3}
        onCropChange={setCrop}
        onZoomChange={setZoom}
        onRotationChange={setRotation}
        onCropComplete={onCropComplete}
        style={{}}
        classes={{}}
        mediaProps={{}}
        cropperProps={{}}
      />
    </div>
  );
};

Capabilities

Cropper Component

The main React component for image and video cropping with interactive controls.

interface CropperProps {
  /** Current crop position */
  crop: Point;
  /** Current zoom level */
  zoom: number;
  /** Current rotation in degrees */
  rotation: number;
  /** Crop aspect ratio */
  aspect: number;
  /** Crop position change callback */
  onCropChange: (location: Point) => void;

  /** Image URL or base64 string */
  image?: string;
  /** Video source(s) */
  video?: string | VideoSrc[];
  /** Custom CSS transform */
  transform?: string;
  /** Minimum zoom level */
  minZoom: number;
  /** Maximum zoom level */
  maxZoom: number;
  /** Shape of crop area */
  cropShape: 'rect' | 'round';
  /** Fixed crop size */
  cropSize?: Size;
  /** Media object fit (default: 'contain') */
  objectFit?: 'contain' | 'cover' | 'horizontal-cover' | 'vertical-cover';
  /** Show crop grid overlay */
  showGrid?: boolean;
  /** Zoom sensitivity */
  zoomSpeed: number;
  /** Enable zoom with mouse wheel */
  zoomWithScroll?: boolean;
  /** Round crop area pixel values */
  roundCropAreaPixels?: boolean;
  /** Restrict crop position within media bounds */
  restrictPosition: boolean;
  /** Keyboard navigation step size */
  keyboardStep: number;

  /** Zoom level change callback */
  onZoomChange?: (zoom: number) => void;
  /** Rotation change callback */
  onRotationChange?: (rotation: number) => void;
  /** Crop interaction complete callback */
  onCropComplete?: (croppedArea: Area, croppedAreaPixels: Area) => void;
  /** Crop area change callback */
  onCropAreaChange?: (croppedArea: Area, croppedAreaPixels: Area) => void;
  /** Crop size change callback */
  onCropSizeChange?: (cropSize: Size) => void;
  /** Interaction start callback */
  onInteractionStart?: () => void;
  /** Interaction end callback */
  onInteractionEnd?: () => void;
  /** Media loaded callback */
  onMediaLoaded?: (mediaSize: MediaSize) => void;

  /** Custom styles */
  style: {
    containerStyle?: React.CSSProperties;
    mediaStyle?: React.CSSProperties;
    cropAreaStyle?: React.CSSProperties;
  };
  /** Custom CSS classes */
  classes: {
    containerClassName?: string;
    mediaClassName?: string;
    cropAreaClassName?: string;
  };

  /** Props passed to media element */
  mediaProps: React.ImgHTMLAttributes<HTMLElement> | React.VideoHTMLAttributes<HTMLElement>;
  /** Props passed to cropper div */
  cropperProps: React.HTMLAttributes<HTMLDivElement>;
  /** Disable automatic CSS injection */
  disableAutomaticStylesInjection?: boolean;
  /** Initial crop area in pixels */
  initialCroppedAreaPixels?: Area;
  /** Initial crop area in percentages */
  initialCroppedAreaPercentages?: Area;
  /** Touch request handler */
  onTouchRequest?: (e: React.TouchEvent<HTMLDivElement>) => boolean;
  /** Wheel request handler */
  onWheelRequest?: (e: WheelEvent) => boolean;
  /** Cropper ref callback */
  setCropperRef?: (ref: React.RefObject<HTMLDivElement>) => void;
  /** Image ref callback */
  setImageRef?: (ref: React.RefObject<HTMLImageElement>) => void;
  /** Video ref callback */
  setVideoRef?: (ref: React.RefObject<HTMLVideoElement>) => void;
  /** Media size callback */
  setMediaSize?: (size: MediaSize) => void;
  /** Crop size callback */
  setCropSize?: (size: Size) => void;
  /** CSP nonce for injected styles */
  nonce?: string;
}

declare const Cropper: React.ComponentType<CropperProps>;
export default Cropper;

Helper Functions

Functions to compute initial crop settings from predefined cropped areas.

/**
 * Compute initial crop position and zoom from pixel coordinates
 * @param croppedAreaPixels - The crop area in pixels
 * @param mediaSize - The media dimensions
 * @param rotation - Current rotation in degrees (default: 0)
 * @param cropSize - The crop area size
 * @param minZoom - Minimum zoom level
 * @param maxZoom - Maximum zoom level
 * @returns Initial crop position and zoom level
 */
function getInitialCropFromCroppedAreaPixels(
  croppedAreaPixels: Area,
  mediaSize: MediaSize,
  rotation?: number,
  cropSize: Size,
  minZoom: number,
  maxZoom: number
): { crop: Point; zoom: number };

/**
 * Compute initial crop position and zoom from percentage coordinates
 * @param croppedAreaPercentages - The crop area in percentages
 * @param mediaSize - The media dimensions
 * @param rotation - Current rotation in degrees (required parameter)
 * @param cropSize - The crop area size
 * @param minZoom - Minimum zoom level
 * @param maxZoom - Maximum zoom level
 * @returns Initial crop position and zoom level
 */
function getInitialCropFromCroppedAreaPercentages(
  croppedAreaPercentages: Area,
  mediaSize: MediaSize,
  rotation: number,
  cropSize: Size,
  minZoom: number,
  maxZoom: number
): { crop: Point; zoom: number };

Types

/** Size dimensions */
interface Size {
  width: number;
  height: number;
}

/** Media size with natural dimensions */
interface MediaSize {
  width: number;
  height: number;
  naturalWidth: number;
  naturalHeight: number;
}

/** Point coordinates */
interface Point {
  x: number;
  y: number;
}

/** Rectangular area definition */
interface Area {
  width: number;
  height: number;
  x: number;
  y: number;
}

/** Video source configuration */
interface VideoSrc {
  src: string;
  type?: string;
}

Usage Examples

Image Cropping with Custom Aspect Ratio

import React, { useState } from 'react';
import Cropper from 'react-easy-crop';
import type { Area, Point } from 'react-easy-crop';

const ImageCropper = ({ imageSrc }: { imageSrc: string }) => {
  const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [croppedArea, setCroppedArea] = useState<Area | null>(null);

  return (
    <div style={{ position: 'relative', width: '100%', height: 400 }}>
      <Cropper
        image={imageSrc}
        crop={crop}
        zoom={zoom}
        aspect={16 / 9}
        onCropChange={setCrop}
        onZoomChange={setZoom}
        onCropComplete={(_, croppedAreaPixels) => setCroppedArea(croppedAreaPixels)}
        showGrid={true}
        cropShape="rect"
        style={{}}
        classes={{}}
        mediaProps={{}}
        cropperProps={{}}
      />
    </div>
  );
};

Video Cropping with Multiple Sources

import React, { useState } from 'react';
import Cropper from 'react-easy-crop';
import type { VideoSrc, Point } from 'react-easy-crop';

const VideoCropper = () => {
  const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  
  const videoSources: VideoSrc[] = [
    { src: '/video.mp4', type: 'video/mp4' },
    { src: '/video.webm', type: 'video/webm' }
  ];

  return (
    <div style={{ position: 'relative', width: '100%', height: 400 }}>
      <Cropper
        video={videoSources}
        crop={crop}
        zoom={zoom}
        aspect={1}
        onCropChange={setCrop}
        onZoomChange={setZoom}
        cropShape="round"
        showGrid={false}
        style={{}}
        classes={{}}
        mediaProps={{}}
        cropperProps={{}}
      />
    </div>
  );
};

Circular Crop with Custom Styling

import React, { useState } from 'react';
import Cropper from 'react-easy-crop';
import type { Point } from 'react-easy-crop';

const CircularCropper = ({ imageSrc }: { imageSrc: string }) => {
  const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);

  return (
    <div style={{ position: 'relative', width: '100%', height: 400 }}>
      <Cropper
        image={imageSrc}
        crop={crop}
        zoom={zoom}
        aspect={1}
        cropShape="round"
        onCropChange={setCrop}
        onZoomChange={setZoom}
        showGrid={false}
        style={{
          containerStyle: {
            backgroundColor: '#333'
          },
          cropAreaStyle: {
            border: '2px solid #fff'
          }
        }}
        classes={{
          containerClassName: 'custom-cropper-container'
        }}
        mediaProps={{}}
        cropperProps={{}}
      />
    </div>
  );
};

Using Initial Crop from Saved Data

import React, { useState, useEffect } from 'react';
import Cropper, { getInitialCropFromCroppedAreaPixels } from 'react-easy-crop';
import type { Area, Point, MediaSize } from 'react-easy-crop';

const CropperWithInitialCrop = ({ imageSrc, savedCropPixels }: { 
  imageSrc: string; 
  savedCropPixels: Area; 
}) => {
  const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [mediaSize, setMediaSize] = useState<MediaSize | null>(null);

  useEffect(() => {
    if (mediaSize && savedCropPixels) {
      const { crop: initialCrop, zoom: initialZoom } = getInitialCropFromCroppedAreaPixels(
        savedCropPixels,
        mediaSize,
        0, // rotation
        { width: 300, height: 200 }, // cropSize
        1, // minZoom
        3  // maxZoom
      );
      setCrop(initialCrop);
      setZoom(initialZoom);
    }
  }, [mediaSize, savedCropPixels]);

  return (
    <div style={{ position: 'relative', width: '100%', height: 400 }}>
      <Cropper
        image={imageSrc}
        crop={crop}
        zoom={zoom}
        aspect={3 / 2}
        onCropChange={setCrop}
        onZoomChange={setZoom}
        onMediaLoaded={setMediaSize}
        cropSize={{ width: 300, height: 200 }}
        style={{}}
        classes={{}}
        mediaProps={{}}
        cropperProps={{}}
      />
    </div>
  );
};