Skip to content

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.

FrameworkInstall
React and Next.jsnpm install @mastershot/editor @mastershot/react-editor
Vue and Nuxtnpm install @mastershot/editor @mastershot/vue-editor
Svelte and SvelteKitnpm install @mastershot/editor @mastershot/svelte-editor
Angularnpm 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

tsx
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

vue
<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:

svelte
<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:

ts
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.

tsx
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.

Stuck, or something isn't working? Email [email protected] with what you tried. If a photo or video won't open, send it if you can, and we'll make it work.