VS

Getting started

@vidstudio/engine is a video editing engine for the browser. It keeps a project (tracks, clips, effects, captions) and plays, renders and exports it on the user's device. @vidstudio/editor is the user interface for that engine: one React component, or one mount function for pages without React.

Three names are used on every page. "The editor" is the component. "The store" is where projects live. "The backend" is the engine object behind one project.

Install

npm install @vidstudio/editor

The engine comes with the editor. react and react-dom 18 or later are peer dependencies. Install @vidstudio/engine alone when you drive the backend without the user interface; see Driving the engine.

Mount in React

Create the store once, create or pick a project, and give the editor a parent with a height.

import { useEffect, useState } from "react";
import { VidstudioEditor, createStore } from "@vidstudio/editor";
import "@vidstudio/editor/editor.css";

type Store = Awaited<ReturnType<typeof createStore>>;

// Defined once: a new options object rebuilds the editor.
const OPTIONS = { namespace: "my-app" };

export function Editor() {
  const [opened, setOpened] = useState<{ store: Store; projectId: string } | null>(null);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      const store = await createStore({ namespace: "my-app" });
      const project = await store.projects.create("My project", { frame_rate: 30 });
      if (!cancelled) setOpened({ store, projectId: project.id });
    })();
    return () => { cancelled = true; };
  }, []);

  if (!opened) return <p>Opening</p>;
  return (
    <div style={{ height: "100vh" }}>
      <VidstudioEditor store={opened.store} projectId={opened.projectId} options={OPTIONS} />
    </div>
  );
}

The editor fills its parent. Keep the options object stable, or the editor rebuilds on every render.

Mount without React

mountEditor renders the editor into any element with a height.

import { createStore, mountEditor, lightTheme } from "@vidstudio/editor";
import "@vidstudio/editor/editor.css";

const store = await createStore({ namespace: "my-app" });
const project = await store.projects.create("My project", { frame_rate: 30 });
const editor = mountEditor(document.getElementById("editor")!, { store, projectId: project.id });

editor.update({ theme: lightTheme });
// Later, when the page is done with it:
editor.unmount();

update replaces only the props you pass. unmount disposes the backend and empties the element.

Next.js

The engine touches browser globals when it loads, so load it on the client only. No bundler setting is needed, with webpack or with Turbopack.

"use client";
import { useEffect, useRef } from "react";
import type { MountedEditor } from "@vidstudio/editor";
import "@vidstudio/editor/editor.css";

export function Editor() {
  const element = useRef<HTMLDivElement>(null);

  useEffect(() => {
    let editor: MountedEditor | null = null;
    let cancelled = false;
    (async () => {
      const { createStore, mountEditor } = await import("@vidstudio/editor");
      const store = await createStore({ namespace: "my-app" });
      const project = await store.projects.create("My project", { frame_rate: 30 });
      if (!cancelled) editor = mountEditor(element.current!, { store, projectId: project.id });
    })();
    return () => { cancelled = true; editor?.unmount(); };
  }, []);

  return <div ref={element} style={{ height: "100vh" }} />;
}

Vite

Set the worker format to ES modules. The engine's workers load their code in chunks, and Vite's default worker format cannot emit chunks.

import { defineConfig } from "vite";

export default defineConfig({
  worker: { format: "es" },
});

The dev server needs no optimizeDeps setting. The workers load from node_modules/@vidstudio/engine/dist/workers/, checked with version 0.1.0 on Vite 5.4.21.

Two folders to serve

Folder What it holds Command
/fonts/ The twelve text fonts cp -r node_modules/@vidstudio/engine/assets/fonts public/fonts
/models/ The speech models for captions, about 450 MB, plus 19 MB for background removal npx vidstudio-fetch-models public/models

Serve them elsewhere and pass the addresses, each ending in a slash: options={{ assets: { fonts: "https://cdn.example.com/fonts/", models: "https://cdn.example.com/models/" } }}. With features={{ captions: false }} no model is ever downloaded. Details, sizes and flags are in Media, fonts and models.

Cross-origin isolation is optional

The editor runs without the headers Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. With them, captions transcribe on several threads, about twice as fast. Import, preview and export are the same either way. The headers also restrict which files your page can load from other origins; see Media, fonts and models.

Licence

Without a licence key the editor works in full on localhost, with a small "vidstudio.app" mark on the preview and on every exported frame. Any other public domain requires a licence key for that domain, which also removes the mark; see Limits and support.

Next pages