Skip to content

mosch/react-avatar-editor

Repository files navigation

react-avatar-editor

npm version Downloads

Avatar / profile picture cropping component for React. Resize, crop and rotate your uploaded image using a simple and clean user interface.

Features

  • Fully typed, written in TypeScript
  • Works with React 17, 18, and 19
  • Resize, crop, and rotate
  • Rounded or square crop area
  • Built-in loading indicator
  • useAvatarEditor hook for easy access to the editor API
  • Zero runtime dependencies

Install

npm i react-avatar-editor

Usage

Basic

import AvatarEditor from 'react-avatar-editor'

function MyEditor() {
  return (
    <AvatarEditor
      image="https://example.com/photo.jpg"
      width={250}
      height={250}
      border={50}
      color={[255, 255, 255, 0.6]}
      scale={1.2}
      rotate={0}
    />
  )
}

With useAvatarEditor hook

The useAvatarEditor hook provides a clean API to access the editor's methods without managing refs manually. All methods return null if the editor isn't ready or no image is loaded.

import AvatarEditor, { useAvatarEditor } from 'react-avatar-editor'

function MyEditor() {
  const editor = useAvatarEditor()

  const handleSave = () => {
    const canvas = editor.getImageScaledToCanvas()
    if (canvas) {
      const dataUrl = canvas.toDataURL()
      // upload dataUrl to your server
    }
  }

  return (
    <div>
      <AvatarEditor
        ref={editor.ref}
        image="https://example.com/photo.jpg"
        width={250}
        height={250}
        border={50}
        scale={1.2}
      />
      <button onClick={handleSave}>Save</button>
    </div>
  )
}

Hook methods

Method Returns Description
ref RefObject Pass this to the ref prop of AvatarEditor.
getImage() HTMLCanvasElement | null The cropped image at the original resolution.
getImageScaledToCanvas() HTMLCanvasElement | null The cropped image scaled to the editor dimensions.
getCroppingRect() object | null The crop area as { x, y, width, height } (0–1 range).

With ref (alternative)

If you prefer using refs directly:

import { useRef } from 'react'
import AvatarEditor, { type AvatarEditorRef } from 'react-avatar-editor'

function MyEditor() {
  const editor = useRef<AvatarEditorRef>(null)

  return (
    <div>
      <AvatarEditor
        ref={editor}
        image="https://example.com/photo.jpg"
        width={250}
        height={250}
      />
      <button
        onClick={() => {
          const canvas = editor.current?.getImageScaledToCanvas()
        }}
      >
        Save
      </button>
    </div>
  )
}

With drag and drop

Using react-dropzone:

import AvatarEditor from 'react-avatar-editor'
import Dropzone from 'react-dropzone'

function MyEditor() {
  const [image, setImage] = useState('https://example.com/photo.jpg')

  return (
    <Dropzone onDrop={([file]) => setImage(file)} noClick noKeyboard>
      {({ getRootProps, getInputProps }) => (
        <div {...getRootProps()}>
          <AvatarEditor width={250} height={250} image={image} />
          <input {...getInputProps()} />
        </div>
      )}
    </Dropzone>
  )
}

Animated rotation

The rotate prop can be animated using any animation library. Here's an example with motion:

import { useState } from 'react'
import { useMotionValue, useSpring, useMotionValueEvent } from 'motion/react'
import AvatarEditor from 'react-avatar-editor'

function MyEditor() {
  const [rotate, setRotate] = useState(0)
  const [animatedRotate, setAnimatedRotate] = useState(0)

  const rotateMotion = useMotionValue(0)
  const rotateSpring = useSpring(rotateMotion, { stiffness: 200, damping: 25 })

  useMotionValueEvent(rotateSpring, 'change', (v) => setAnimatedRotate(v))

  if (rotateMotion.get() !== rotate) {
    rotateMotion.set(rotate)
  }

  return (
    <>
      <AvatarEditor
        image="https://example.com/photo.jpg"
        rotate={animatedRotate}
      />
      <button onClick={() => setRotate((r) => r - 90)}></button>
      <button onClick={() => setRotate((r) => r + 90)}></button>
    </>
  )
}

Props

Prop Type Default Description
image string | File The URL or File object of the image to edit.
width number 200 Width of the crop area in pixels.
height number 200 Height of the crop area in pixels.
border number | number[] 25 Border size around the crop area. Use an array [horizontal, vertical] for different values.
borderRadius number 0 Border radius of the crop area. Set to width / 2 for a circle.
color number[] [0, 0, 0, 0.5] RGBA color of the crop mask overlay.
borderColor number[] RGBA color of the 1px border around the crop area. No border if omitted.
backgroundColor string Background color for transparent images (CSS color string).
scale number 1 Zoom level. 1 = fit, > 1 = zoom in, < 1 = zoom out (requires disableBoundaryChecks).
rotate number 0 Rotation in degrees.
position { x, y } Center of the crop area (0–1 range). Set this + onPositionChange for controlled panning.
style CSSProperties Additional CSS styles for the canvas element.
crossOrigin string crossOrigin attribute for the image. Use "anonymous" for CORS images.
showGrid boolean false Show a rule-of-thirds grid overlay.
gridColor string "#666" Color of the grid lines.
disableBoundaryChecks boolean false Allow the image to be moved outside the crop boundary.
disableHiDPIScaling boolean false Disable devicePixelRatio scaling. Can improve performance on mobile.
disableCanvasRotation boolean true When false, the canvas resizes to fit the rotated image.
onLoadStart () => void Called when image loading begins.
onLoadSuccess (image) => void Called when the image loads successfully.
onLoadFailure () => void Called when the image fails to load.
onImageReady () => void Called when the image is first painted on the canvas.
onImageChange () => void Called on every visual change (drag, scale, rotate, etc.).
onMouseUp () => void Called when the user releases the mouse after dragging.
onMouseMove (event) => void Called on every mouse/touch move while dragging.
onPositionChange (position) => void Called when the crop position changes. Receives { x, y }.
onRequestScaleChange (scale) => void Called when the user presses +/- keys to zoom. Receives the requested new scale value.
keyboardStep number 1 Pixels to move per arrow key press. Shift multiplies by 10.

Contributing

pnpm install          # install dependencies
pnpm build            # build the library
pnpm lint             # run oxlint
pnpm fmt              # format with oxfmt
pnpm demo:dev         # run demo at localhost:3000

Kudos

Thanks to all contributors:

dan-lee, mtlewis, jakerichan, hu9o, ggwzrd, nmn, kukagg, benwiley4000, ruipserra, rdw, sktt, RKJuve, tibotiber, aumayr, yamafaktory, codedmart, chengyin, MahdiHadrich, jyash97, fivenp, pvcresin, shakaman, oyeanuj, dev-nima, kpbp, DedaDev, vitbokisch, pekq, MateusZitelli, kimon89, xaviergonz, jbrumwell, luisrudge, bytor99999, Mitelak, sahanDissanayake, sle-c, YacheLee, yogthesharma, taro-shono, tanguyantoine, lulzsun, mloeks, metacortex, exiify, thinhvoxuan, tvankith, yarikgenza, zhipenglin, TheMcMurder, notjosh, xulww, jimniels, jeffkole, deadlyicon, velezjose, lixiaoyan, brigand, florapdx, kuhelbeher, chris-rudmin, bluej100, dehbmarques, kimorq

About

Small avatar & profile picture component. Resize and crop uploaded images using a intuitive user interface.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors