React, Vue, Svelte and Angular
Add an image and video editor to a React, Vue, Svelte or Angular app: install it from npm and use the component for your framework, with TypeScript types.
The editor is on npm as @mastershot/editor, with TypeScript types, and each framework has a package with a component to install beside it. Every option is a prop or an input, and every event is an on… prop or an output. The editor loads only in the browser, once the component is on the page, so server rendering never runs it.
| Framework | Install |
|---|---|
| React and Next.js | npm install @mastershot/editor @mastershot/react-editor |
| Vue and Nuxt | npm install @mastershot/editor @mastershot/vue-editor |
| Svelte and SvelteKit | npm install @mastershot/editor @mastershot/svelte-editor |
| Angular | npm install @mastershot/editor @mastershot/angular-editor |
The packages are the same editor as the script on our CDN, at the same version, with everything it needs inside them, so they never load files from anywhere else. There's no stylesheet to import.
If your site has a Content Security Policy, allow data: in font-src (the editor's font), blob: in worker-src (its encoders), and https://mastershot.app in connect-src (the license check).
React and Next.js
import { MastershotEditor } from "@mastershot/react-editor"
export function PhotoEditor({
file,
onSave,
}: {
file: File
onSave: (file: File) => void
}) {
return (
<MastershotEditor
src={file}
utils={["crop", "resize"]}
licenseKey={process.env.NEXT_PUBLIC_MASTERSHOT_KEY}
onProcess={({ dest }) => onSave(dest)}
style={{ height: 600 }}
/>
)
}Changing a prop changes the editor straight away, with the edits kept, and a new src opens that file. To call the editor's methods, give the component a ref: ref.current.editor is the editor. In Next.js, put "use client" at the top of the file.
Vue and Nuxt
<script setup>
import { MastershotEditor } from "@mastershot/vue-editor"
defineProps({ file: File })
const emit = defineEmits(["save"])
</script>
<template>
<MastershotEditor
:src="file"
:utils="['crop', 'resize']"
:image-crop-aspect-ratio="1"
@process="({ dest }) => emit('save', dest)"
style="height: 600px"
/>
</template>Options can be written in kebab-case or camelCase. A template ref's editor is the editor. In Nuxt, wrap the component in <ClientOnly>.
Svelte
The editor is an action, which works in Svelte 4 and 5:
<script>
import { mastershotEditor } from "@mastershot/svelte-editor"
let { file, onsave } = $props()
</script>
<div
use:mastershotEditor={{ src: file, utils: ["crop"], onprocess: ({ dest }) => onsave(dest) }}
style="height: 600px"
></div>When the object changes, the editor changes with it. The element gets an editor event with the editor once it has loaded.
Angular
A standalone component, for Angular 17 and up, with or without zone.js:
import { Component } from "@angular/core"
import { MastershotEditorComponent } from "@mastershot/angular-editor"
import type { WriterOutput } from "@mastershot/editor"
@Component({
selector: "app-photo-editor",
standalone: true,
imports: [MastershotEditorComponent],
template: `
<mastershot-editor
[src]="file"
[options]="{ utils: ['crop', 'resize'], imageCropAspectRatio: 1 }"
licenseKey="MSE-…"
(process)="save($event)"
style="height: 600px"
/>
`,
})
export class PhotoEditorComponent {
file?: File
save({ dest }: WriterOutput) {
// dest is the edited File
}
}[options] takes every option, and each event is an output of the same name, such as (process) or (load). The component's editor is the editor itself. Don't also register the <mastershot-editor> HTML element in an Angular app; the component uses that name.
From the CDN instead
Without a build step, or to keep the editor out of your bundle, import it from its URL with a dynamic import() once the component is on the page, and call destroy() when it goes away. The comments in the import() tell Vite and webpack to leave the URL alone.
import { useEffect, useRef } from "react"
const editorUrl = "https://cdn.mastershot.app/editor/1.8.0/editor.js"
export function PhotoEditor({ file }: { file: File }) {
const box = useRef<HTMLDivElement>(null)
useEffect(() => {
let editor: { destroy(): void } | undefined
let gone = false
import(/* @vite-ignore */ /* webpackIgnore: true */ editorUrl).then(
({ appendDefaultEditor }) => {
if (gone || !box.current) return
editor = appendDefaultEditor(box.current, { src: file })
}
)
return () => {
gone = true
editor?.destroy()
}
}, [file])
return <div ref={box} style={{ height: 600 }} />
}Others
Anything that can run code once an element is on the page can host the editor: import { appendDefaultEditor } from "@mastershot/editor", call appendDefaultEditor(element, options) then, and destroy() when the element goes. The <mastershot-editor> element works too, in any framework that renders custom elements. See Add the editor to your site.
TypeScript
The npm packages have types for every option, event and method, so the editor and its components are checked as you write. Importing from the CDN's URL has no types, so TypeScript sees what import() returns as any; import type { Editor } from "@mastershot/editor" gives you them there too.
Changing the file
To open a different file in an editor that's already there, call editor.loadImage(file) instead of making a new editor. Changing options such as utils or imageCropAspectRatio on the editor takes effect straight away, with the edits kept.