
# Studio SDK

[![View on GitHub](https://img.shields.io/badge/GitHub-View_on_GitHub-blue?logo=github)](https://github.com/shotstack/shotstack-studio-sdk)
[![npm version](https://img.shields.io/npm/v/@shotstack/shotstack-studio.svg)](https://www.npmjs.com/package/@shotstack/shotstack-studio)

A JavaScript SDK for browser-based video editing with timeline, canvas preview, and export. It provides a fully
interactive editing environment that works with the [Shotstack data model](/docs/guide/getting-started/core-concepts/#edit).

## Interactive Examples

Try the Shotstack Studio in your preferred framework:

[![TypeScript](https://img.shields.io/badge/TypeScript-StackBlitz-blue?style=for-the-badge&logo=typescript)](https://stackblitz.com/fork/github/shotstack/shotstack-studio-sdk-demos/tree/master/typescript)
[![React](https://img.shields.io/badge/React-StackBlitz-blue?style=for-the-badge&logo=react)](https://stackblitz.com/fork/github/shotstack/shotstack-studio-sdk-demos/tree/master/react)
[![Vue](https://img.shields.io/badge/Vue-StackBlitz-blue?style=for-the-badge&logo=vue.js)](https://stackblitz.com/fork/github/shotstack/shotstack-studio-sdk-demos/tree/master/vue)
[![Angular](https://img.shields.io/badge/Angular-StackBlitz-blue?style=for-the-badge&logo=angular)](https://stackblitz.com/fork/github/shotstack/shotstack-studio-sdk-demos/tree/master/angular)
[![Next.js](https://img.shields.io/badge/Next.js-StackBlitz-blue?style=for-the-badge&logo=next.js)](https://stackblitz.com/fork/github/shotstack/shotstack-studio-sdk-demos/tree/master/nextjs)

## Create a new project

The fastest way to get started is to scaffold a project. This creates a small app with the Studio SDK already set up, including the canvas, timeline, and controls. It comes pre-loaded with a starter [Edit](/docs/guide/getting-started/core-concepts/#edit) that you can customise.

```bash
npm create video-editor@latest
# or
npx create-video-editor
```

You'll be asked for a project name and a framework, then:

```bash
cd my-video-editor
npm install
npm run dev
```

`npm run dev` starts the development server and opens a live, editable video editor in your browser. The Studio SDK runs inside your own app, which you can customise and deploy.

:::tip
The starter Edit lives in `src/template.json`. This is the same Edit JSON that the [Edit API](https://shotstack.io/docs/api/#tocs_edit) renders, so you can design it in the browser and then render a final MP4 with the CLI: `npx shotstack render src/template.json`.
:::

### Frameworks

| Choose | Stack |
| --- | --- |
| `react` | React 19 + Vite |
| `vue` | Vue 3 + Vite |
| `nextjs` | Next.js |
| `angular` | Angular |
| `typescript` | Vanilla TypeScript + Vite |

To skip the prompts, pass the project directory and framework directly:

```bash
npx create-video-editor my-editor --template react --yes
```

## Installation

To add the SDK to an existing app, install it directly:

```bash
npm install @shotstack/shotstack-studio
```

```bash
yarn add @shotstack/shotstack-studio
```

## Quick Start

```typescript
import { Edit, Canvas, Controls, Timeline, UIController } from "@shotstack/shotstack-studio";

// 1) Load a template
const response = await fetch("https://shotstack-assets.s3.amazonaws.com/templates/hello-world/hello.json");
const template = await response.json();

// 2) Create core components
const edit = new Edit(template);
const canvas = new Canvas(edit);
const ui = UIController.create(edit, canvas);

// 3) Load canvas and edit
await canvas.load();
await edit.load();

// 4) Register toolbar buttons
ui.registerButton({
  id: "text",
  icon: `<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3H13"/><path d="M8 3V13"/><path d="M5 13H11"/></svg>`,
  tooltip: "Add Text"
});

// 5) Handle button clicks
ui.on("button:text", ({ position }) => {
  edit.addTrack(0, {
    clips: [
      {
        asset: {
          type: "rich-text",
          text: "Title",
          font: { family: "Work Sans", size: 72, weight: 600, color: "#ffffff", opacity: 1 },
          align: { horizontal: "center", vertical: "middle" }
        },
        start: position,
        length: 5,
        width: 500,
        height: 200
      }
    ]
  });
});

// 6) Initialize the Timeline
const timelineContainer = document.querySelector("[data-shotstack-timeline]") as HTMLElement;
const timeline = new Timeline(edit, timelineContainer);
await timeline.load();

// 7) Add keyboard controls
const controls = new Controls(edit);
await controls.load();

// 8) Add event handlers
edit.events.on("clip:selected", data => {
  console.log("Clip selected:", data);
});
```

Your HTML must include both containers:

```html
<div data-shotstack-studio></div>
<div data-shotstack-timeline></div>
```

## Main Components

### Edit

`Edit` is the runtime editing session and source of truth for document mutations.

```typescript
import { Edit } from "@shotstack/shotstack-studio";

const edit = new Edit(templateJson);
await edit.load();

await edit.loadEdit(nextTemplateJson);

// Playback (seconds)
edit.play();
edit.pause();
edit.seek(2);
edit.stop();

// Mutations
await edit.addTrack(0, { clips: [] });
await edit.addClip(0, {
  asset: { type: "image", src: "https://example.com/image.jpg" },
  start: 0,
  length: 5
});
await edit.updateClip(0, 0, { length: 6 });
await edit.deleteClip(0, 0);

// History
await edit.undo();
await edit.redo();

// Clip operations
await edit.deleteTrack(0);

// Output settings
await edit.setOutputSize(1920, 1080);
await edit.setOutputFps(30);
await edit.setOutputFormat("mp4");
await edit.setOutputResolution("hd");
await edit.setOutputAspectRatio("16:9");
await edit.setTimelineBackground("#000000");

// Read state
const time = edit.playbackTime;
const playing = edit.isPlaying;
const clip = edit.getClip(0, 0);
const track = edit.getTrack(0);
const snapshot = edit.getEdit();
const durationSeconds = edit.totalDuration;
```

#### Events

Listen using string event names:

```typescript
const unsubscribeClipSelected = edit.events.on("clip:selected", data => {
  console.log("Selected clip", data.trackIndex, data.clipIndex);
});

edit.events.on("clip:updated", data => {
  console.log("Updated from", data.previous, "to", data.current);
});

edit.events.on("playback:play", () => {
  console.log("Playback started");
});

// Unsubscribe when no longer needed
unsubscribeClipSelected();
```

Available event names:

| Category | Event Names |
| --- | --- |
| Playback | `playback:play`, `playback:pause` |
| Timeline | `timeline:updated`, `timeline:backgroundChanged` |
| Clip lifecycle | `clip:added`, `clip:selected`, `clip:updated`, `clip:deleted`, `clip:restored`, `clip:copied`, `clip:loadFailed`, `clip:unresolved` |
| Selection | `selection:cleared` |
| Edit state | `edit:changed`, `edit:undo`, `edit:redo` |
| Track | `track:added`, `track:removed` |
| Duration | `duration:changed` |
| Output | `output:resized`, `output:resolutionChanged`, `output:aspectRatioChanged`, `output:fpsChanged`, `output:formatChanged`, `output:destinationsChanged` |
| Merge fields | `mergefield:changed` |

### Canvas

`Canvas` renders the current edit.

```typescript
import { Canvas } from "@shotstack/shotstack-studio";

const canvas = new Canvas(edit);
await canvas.load();

canvas.centerEdit();
canvas.zoomToFit();
canvas.setZoom(1.25);
canvas.resize();
const zoom = canvas.getZoom();
canvas.dispose();
```

### UIController

`UIController` manages built-in UI wiring and extensible button events.

```typescript
import { UIController } from "@shotstack/shotstack-studio";

const ui = UIController.create(edit, canvas, { mergeFields: true });

ui.registerButton({
  id: "add-title",
  icon: `<svg viewBox="0 0 16 16">...</svg>`,
  tooltip: "Add Title"
});

const unsubscribe = ui.on("button:add-title", ({ position }) => {
  console.log("Button clicked at", position, "seconds");
});

ui.unregisterButton("add-title");
unsubscribe();
ui.dispose();
```

### Timeline

`Timeline` provides visual clip editing.

```typescript
import { Timeline } from "@shotstack/shotstack-studio";

const container = document.querySelector("[data-shotstack-timeline]") as HTMLElement;
const timeline = new Timeline(edit, container);

await timeline.load();
timeline.zoomIn();
timeline.zoomOut();
timeline.dispose();
```

### Controls

`Controls` enables keyboard playback and edit shortcuts.

```typescript
import { Controls } from "@shotstack/shotstack-studio";

const controls = new Controls(edit);
await controls.load();
```

Available keyboard shortcuts:

| Key | Action |
| --- | --- |
| Space | Play / Pause |
| J | Stop |
| K | Pause |
| L | Play |
| Left Arrow | Seek backward |
| Right Arrow | Seek forward |
| Shift + Arrow | Seek larger amount |
| Comma | Step backward one frame |
| Period | Step forward one frame |

### VideoExporter

`VideoExporter` exports a timeline render from the browser runtime.

```typescript
import { VideoExporter } from "@shotstack/shotstack-studio";

const exporter = new VideoExporter(edit, canvas);
await exporter.export("my-video.mp4", 25);
```

## Custom UI Buttons

Use `UIController` to register and handle custom button actions.

```typescript
ui.registerButton({
  id: "text",
  icon: `<svg viewBox="0 0 16 16">...</svg>`,
  tooltip: "Add Text",
  dividerBefore: true
});

ui.on("button:text", ({ position, selectedClip }) => {
  console.log("Current time (seconds):", position);
  console.log("Current selection:", selectedClip);
});

ui.unregisterButton("text");
```

## API Reference

For schema-level details and type definitions, see the [Shotstack API Reference](https://shotstack.io/docs/api/#tocs_edit).
