CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-easy-crop

A React component to crop images/videos with easy interactions

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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>
  );
};
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/react-easy-crop@5.5.x
Publish Source
CLI
Badge
tessl/npm-react-easy-crop badge