Guides

* [Getting started](/developers)
* [Projects and storage](/developers/projects-and-storage)
* [Media, fonts and models](/developers/media)
* [Save and load](/developers/save-and-load)
* [Theme, features and events](/developers/theming-and-events)
* [Driving the engine](/developers/engine)
* [Limits and support](/developers/limits-and-support)
* [Troubleshooting](/developers/troubleshooting)

More

* [Reference](/developers/reference/)
* [Everything as one text file](/developers/llms-full.txt)

# 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](/developers/engine).

## Mount in React

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

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

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

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

```ts
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](/developers/media).

## 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](/developers/media).

## 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](/developers/limits-and-support).

## Next pages

* [Projects and storage](/developers/projects-and-storage): the store, namespaces, where data lives, quotas, two editors on one page.
* [Media, fonts and models](/developers/media): what imports, proxies, web addresses, fonts and models.
* [Save and load](/developers/save-and-load): saving a project with its media to your server and loading it back.
* [Theme, features and events](/developers/theming-and-events): theme keys, feature flags, shortcuts, events.
* [Driving the engine](/developers/engine): the backend without the user interface.
* [Limits and support](/developers/limits-and-support): browsers, sizes, the licence, help.
* [Troubleshooting](/developers/troubleshooting): symptoms and fixes.
* [The reference](/developers/reference/): every type and method.

---
Source: [https://vidstudio.app/developers](https://vidstudio.app/developers)
