
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
react-photo-editor
Advanced tools
React component and hook for image editing, offering controls for brightness, contrast, saturation, and grayscale, along with features to rotate, flip, pan, draw, and zoom photos.

React component and hook for image editing with options to set brightness, contrast, saturation, and grayscale. Also with features to rotate, flip, pan, draw and zoom the photo.
usePhotoEditor hookChoose your preferred package manager:
# Using npm
npm install react-photo-editor
# Using yarn
yarn add react-photo-editor
import { useState } from 'react';
import { ReactPhotoEditor } from 'react-photo-editor';
function App() {
const [file, setFile] = useState();
const [showModal, setShowModal] = useState(false);
// Show modal if file is selected
const showModalHandler = () => {
if (file) {
setShowModal(true);
}
};
// Hide modal
const hideModal = () => {
setShowModal(false);
};
// Save edited image
const handleSaveImage = (editedFile) => {
setFile(editedFile);
// Do something with the edited file
};
const setFileData = (e) => {
if (e?.target?.files && e.target.files.length > 0) {
setFile(e.target.files[0]);
}
};
return (
<>
<input type='file' onChange={(e) => setFileData(e)} multiple={false} />
<button onClick={showModalHandler}>Edit Photo</button>
<ReactPhotoEditor
open={showModal}
onClose={hideModal}
file={file}
onSaveImage={handleSaveImage}
/>
</>
);
}
export default App;
See it in action on Stackblitz
import 'react-photo-editor/dist/style.css') is no longer required'./node_modules/react-photo-editor/dist/*.js' to your Tailwind configcanvas to rpe-canvas for better consistency with the rest of the project. Make sure to update your usage accordingly.The ReactPhotoEditor component accepts the following props:
| Prop | Type | Default | Description |
|---|---|---|---|
file | File | undefined | Required | The input image file to be edited |
allowColorEditing | boolean | true | Whether to allow color editing options |
allowRotate | boolean | true | Whether to allow rotation of the image |
allowFlip | boolean | true | Whether to allow flipping of the image |
allowZoom | boolean | true | Whether to allow zooming of the image |
allowDrawing | boolean | true | Whether to enable drawing options |
downloadOnSave | boolean | false | Whether to enable downloading the edited image upon saving |
open | boolean | false | Whether the photo editor modal is open |
onClose | () => void | - | Function invoked when the modal is closed |
onSaveImage | (image: File) => void | Required | Function invoked when the edited image is saved |
modalHeight | number | string | '38rem' | Height of the photo editor modal |
modalWidth | number | string | '40rem' | Width of the photo editor modal |
canvasWidth | number | string | 'auto' | Width of the canvas element |
canvasHeight | number | string | 'auto' | Height of the canvas element |
maxCanvasHeight | number | string | '22rem' | Maximum height of the canvas element |
maxCanvasWidth | number | string | '36rem' | Maximum width of the canvas element |
labels | ReactPhotoEditorTranslations | - | Custom labels for UI elements |
usePhotoEditor HookFor more control over the UI and functionality, you can use the usePhotoEditor hook to build your own custom editor component.
For full examples, see the example folder.
import { usePhotoEditor } from 'react-photo-editor';
function CustomPhotoEditor({ file }) {
const {
canvasRef,
brightness,
contrast,
saturate,
grayscale,
setBrightness,
setContrast,
setSaturate,
setGrayscale,
handleZoomIn,
handleZoomOut,
generateEditedFile,
resetFilters,
} = usePhotoEditor({
file,
defaultBrightness: 100,
defaultContrast: 100,
defaultSaturate: 100,
defaultGrayscale: 0,
});
const handleSave = async () => {
const editedFile = await generateEditedFile();
// Do something with the edited file
};
return (
<div>
<canvas ref={canvasRef} />
<div>
<label>Brightness: {brightness}</label>
<input
type='range'
min='0'
max='200'
value={brightness}
onChange={(e) => setBrightness(Number(e.target.value))}
/>
</div>
{/* Add more controls for other parameters */}
<button onClick={handleZoomIn}>Zoom In</button>
<button onClick={handleZoomOut}>Zoom Out</button>
<button onClick={resetFilters}>Reset</button>
<button onClick={handleSave}>Save</button>
</div>
);
}
| Parameter | Type | Default | Description |
|---|---|---|---|
file | File | undefined | - | The image file to be edited |
defaultBrightness | number | 100 | Initial brightness level |
defaultContrast | number | 100 | Initial contrast level |
defaultSaturate | number | 100 | Initial saturation level |
defaultGrayscale | number | 0 | Initial grayscale level |
defaultFlipHorizontal | boolean | false | Initial horizontal flip state |
defaultFlipVertical | boolean | false | Initial vertical flip state |
defaultZoom | number | 1 | Initial zoom level |
defaultRotate | number | 0 | Initial rotation angle in degrees |
defaultLineColor | string | #000000 | Initial line color in hex code |
defaultLineWidth | number | 2 | Initial line/stroke width |
defaultMode | string | pan | Initial mode (draw or move) |
The hook returns the following values and functions:
| Name | Type | Description |
|---|---|---|
canvasRef | RefObject<HTMLCanvasElement> | Reference to the canvas element |
imageSrc | string | The source of the loaded image |
| State | Setter | Type | Description |
|---|---|---|---|
brightness | setBrightness | number | Brightness level (default 100) |
contrast | setContrast | number | Contrast level (default 100) |
saturate | setSaturate | number | Saturation level (default 100) |
grayscale | setGrayscale | number | Grayscale level (default 0) |
rotate | setRotate | number | Rotation angle in degrees |
zoom | setZoom | number | Zoom level |
flipHorizontal | setFlipHorizontal | boolean | Flip horizontally |
flipVertical | setFlipVertical | boolean | Flip vertically |
mode | setMode | 'draw' | 'move' | Interaction mode (draw or move) |
lineColor | setLineColor | string | Drawing line color |
lineWidth | setLineWidth | number | Drawing line width |
| Function | Type | Description |
|---|---|---|
handleZoomIn | () => void | Zoom in |
handleZoomOut | () => void | Zoom out |
resetFilters | () => void | Reset all filters and transformations |
downloadImage | () => void | Download current canvas as an image |
generateEditedFile | () => Promise<File> | Returns the edited canvas as a File object |
| Function | Type | Description |
|---|---|---|
handlePointerDown | PointerEventHandler | Used for drawing |
handlePointerUp | PointerEventHandler | Used for drawing |
handlePointerMove | PointerEventHandler | Used for drawing |
handleWheel | WheelEventHandler | Used for zooming |
Contributions to react-photo-editor are welcome! If you have any issues, feature requests, or improvements, please open an issue or submit a pull request on the GitHub repository.
git checkout -b feature/amazing-featuregit commit -m 'Add some amazing feature'git push origin feature/amazing-featureWhen reporting issues, please provide:
This project is licensed under the MIT License - see the LICENSE file for details.
FAQs
React component and hook for image editing, offering controls for brightness, contrast, saturation, and grayscale, along with features to rotate, flip, pan, draw, and zoom photos.
The npm package react-photo-editor receives a total of 1,309 weekly downloads. As such, react-photo-editor popularity was classified as popular.
We found that react-photo-editor demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.