React, Vue, Svelte and others
Load the editor in a component, give it the file, and remove it when the component goes away.
The editor loads from its URL rather than from npm, so in a framework you import it 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.
React and Next.js
import { useEffect, useRef } from "react"
const editorUrl = "https://cdn.mastershot.app/editor/1.1.0/editor.js"
export function PhotoEditor({
file,
onSave,
}: {
file: File
onSave: (file: File) => void
}) {
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,
licenseKey: process.env.NEXT_PUBLIC_MASTERSHOT_KEY,
})
editor.on("process", ({ dest }: { dest: File }) => onSave(dest))
}
)
return () => {
gone = true
editor?.destroy()
}
}, [file])
return <div ref={box} style={{ height: 600 }} />
}In Next.js, put "use client" at the top of the file. The editor runs only in the browser.
Vue
<script setup>
import { onMounted, onBeforeUnmount, ref } from "vue"
const props = defineProps({ file: File })
const emit = defineEmits(["save"])
const box = ref()
let editor
onMounted(async () => {
const { appendDefaultEditor } = await import(
/* @vite-ignore */ "https://cdn.mastershot.app/editor/1.1.0/editor.js"
)
editor = appendDefaultEditor(box.value, { src: props.file })
editor.on("process", ({ dest }) => emit("save", dest))
})
onBeforeUnmount(() => editor?.destroy())
</script>
<template>
<div ref="box" style="height: 600px" />
</template>In Nuxt, wrap the component in <ClientOnly>.
Svelte
<script>
import { onMount } from "svelte"
let { file, onsave } = $props()
let box
onMount(() => {
let editor
import(/* @vite-ignore */ "https://cdn.mastershot.app/editor/1.1.0/editor.js").then(
({ appendDefaultEditor }) => {
editor = appendDefaultEditor(box, { src: file })
editor.on("process", ({ dest }) => onsave(dest))
}
)
return () => editor?.destroy()
})
</script>
<div bind:this={box} style="height: 600px"></div>Angular and others
Anything that can run code once an element is on the page can host the 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 script has no type declarations yet, so TypeScript sees what import() returns as any. The reference lists everything it has.
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.