Vue 3 image editor component for Nextcloud apps. Replacement for the unmaintained Filerobot editor.
prefers-reduced-motion respectedBlob at natural resolution in PNG, JPEG or WebP, optionally
bounded by maxSize; an unedited image is handed back untouchedEditorState;
the Konva scene is a pure render of it, and the export runs through the
same code path as the interactive view: what you save is what you saw.Blob, File or URL)
and emits an edited Blob. WebDAV, versioning and file naming belong to the
consuming app.Install the peer dependencies alongside it:
npm install @nextcloud/image-editor @nextcloud/vue @nextcloud/dialogs
The component's styles come with it: the built module imports its own
stylesheet, so any bundler that handles CSS imports from dependencies
picks them up. Where yours does not, load them yourself from the
@nextcloud/image-editor/style export.
That import is also why the package has to go through a bundler. Loading
it in a plain Node process, for server-side rendering or a script, fails
with a syntax error as Node tries to parse the stylesheet as JavaScript.
This matches @nextcloud/vue, which imports its own CSS the same way,
and every Nextcloud app bundles with vite or webpack. See #5.
<ImageEditor>| Prop | Type | Description |
|---|---|---|
src |
Blob | string |
Image to edit (Blob, File or URL). Required. |
label |
string |
Accessible label of the canvas area. |
exportOptions |
ExportOptions |
format, quality and maxSize for the save button. Defaults to PNG at natural resolution. |
initialState |
EditorState |
State to open with, as emitted by change, for resuming an unfinished edit. Read when the source loads. |
saving |
boolean |
Raise while your app stores the saved image. The editor shows the same progress it shows for its own export, so one indicator covers the whole wait. |
| Event | Payload | Description |
|---|---|---|
save |
ExportResult |
Edited image rendered at natural resolution. |
cancel |
– | User dismissed the editor. |
error |
Error |
Loading or export failed. |
change |
EditorState |
Fired when an edit is committed, e.g. for dirty tracking. A slider being dragged previews without emitting; releasing it emits once. |
Exposed methods:
exportImage(options?: ExportOptions): Promise<ExportResult> with
format, quality and maxSize (longest edge bound) options.reset(state?: EditorState) to start over, optionally from a given
state.Pair change with initialState to resume an edit across a reload: store
what change reports, hand it back as initialState, and the editor opens
where the user left off.
Saving an image that was not edited hands back the source bytes
untouched, rather than re-encoding them. That keeps the file's quality
and its metadata, and it only applies when src was given as a Blob
or File, nothing was asked of the encoder, and the state is pristine.
isPristine(state) is exported for the same check, e.g. to disable a
save button.
useHistory<T>(capacity?)Linear undo/redo history of immutable snapshots, used by the editor and
exported for standalone use. Each step carries an optional label, and
entries, index and jumpTo() let a consumer render a history list
and move to any step in it. Snapshots are held by reference: whatever
is pushed must not be mutated afterwards.
npm ci
npm run test # unit tests (vitest)
npm run test:e2e # Playwright tests (real browser, canvas)
npm run playground # dev playground at http://localhost:5173
npm run lint
npm run typecheck # vue-tsc over lib/ and __tests__/
npm run build
npm run build:doc # typedoc API documentation
npm run build:demo # static demo page build
Regressions are the primary risk for a long-lived canvas library: