From fe290e300198ff0a157a2110768e5d89e86cc85a Mon Sep 17 00:00:00 2001 From: Alex Hixon Date: Fri, 13 Mar 2026 13:08:20 +1100 Subject: [PATCH] fix(@uppy/image-editor): destroy cropper and clean up state on file re-open and removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When opening the image editor for a second file (e.g. drag file → save in editor → clear → drag new file), the old Cropper instance was not destroyed. This caused `initCropper()` to bail early (`if (this.cropper) return`), leaving the new image with a stale cropper that retained the previous file's crop dimensions and referenced a destroyed DOM element. Additionally, when a file was removed from the Dashboard, the ImageEditor plugin had no handler to clean up its internal state (currentImage, cropper, objectUrl), so everything persisted into the next editing session. Changes: - Call `this.destroyCropper()` at the top of `start()` to tear down any previous cropper before initializing a new editing session - Add a `file-removed` event listener (registered in `install()`, cleaned up in `uninstall()`) that calls `stop()` when the currently-edited file is removed from Uppy --- .changeset/fix-image-editor-cleanup.md | 5 +++++ packages/@uppy/image-editor/src/ImageEditor.tsx | 10 ++++++++++ 2 files changed, 15 insertions(+) create mode 100644 .changeset/fix-image-editor-cleanup.md diff --git a/.changeset/fix-image-editor-cleanup.md b/.changeset/fix-image-editor-cleanup.md new file mode 100644 index 000000000..a0be3cb5f --- /dev/null +++ b/.changeset/fix-image-editor-cleanup.md @@ -0,0 +1,5 @@ +--- +"@uppy/image-editor": patch +--- + +Fix cropper not reinitializing after save/cancel by destroying previous instance in `start()` and cleaning up on file removal diff --git a/packages/@uppy/image-editor/src/ImageEditor.tsx b/packages/@uppy/image-editor/src/ImageEditor.tsx index 74ed79476..b0fa2f4ea 100644 --- a/packages/@uppy/image-editor/src/ImageEditor.tsx +++ b/packages/@uppy/image-editor/src/ImageEditor.tsx @@ -350,6 +350,7 @@ export default class ImageEditor< */ start = (file: UppyFile): void => { // Clean up any previous editing session + this.destroyCropper() if (this.objectUrl) { URL.revokeObjectURL(this.objectUrl) this.objectUrl = null @@ -480,8 +481,16 @@ export default class ImageEditor< return this.objectUrl } + handleFileRemoved = (file: UppyFile): void => { + const { currentImage } = this.getPluginState() + if (currentImage && currentImage.id === file.id) { + this.stop() + } + } + install(): void { this.resetEditorState(null) + this.uppy.on('file-removed', this.handleFileRemoved) const { target } = this.opts if (target) { @@ -496,6 +505,7 @@ export default class ImageEditor< const file = this.uppy.getFile(currentImage.id) this.uppy.emit('file-editor:cancel', file) } + this.uppy.off('file-removed', this.handleFileRemoved) this.stop() this.unmount() }