Compose beta

Compose turns a local JSX composition into a product video (MP4 + WebM + poster), uploads it for human review, and serves the approved render from a durable CDN URL — the same "only flips after approval" guarantee as screenshots, for video.

Prerequisites

  • The Reshot CLI: npm i -D @reshotdev/screenshot (or npx @reshotdev/screenshot).
  • ffmpeg on your PATH — the renderer transcodes frames to MP4/WebM with it.
  • A project + publish token (from onboarding, or Settings → Integrations).

The three files of a composition

reshot compose renders from three inputs that live next to each other:

FileWhat it isHow you get it
<name>.compose.tsxThe JSX scene — overlays, labels, timingAuthored by you (scaffolded by reshot record)
<name>.metadata.jsonRequired sibling — the workflow timeline, capture size, and target rectsWritten by reshot record alongside the capture
capture video (e.g. login-capture.mp4)The screen recording the scene annotates, referenced by capturePathProduced by reshot record

The <name>.metadata.json sibling is mandatory. reshot compose Foo.compose.tsx reads Foo.metadata.json for the timeline/targets and errors if it is missing.

Authoring a scene

A composition imports from @reshot/compose and wraps a <ProductFilm> in a <Composition>:

You don't npm install @reshot/compose — the CLI bundles the render engine and resolves that import for you when reshot compose builds your scene. The import is for authoring only. Keep your .compose.tsx, .metadata.json, and capture video in the same folder; don't import "./x.metadata.json" into the scene — the CLI bundles the .compose.tsx from a temp dir, so a relative file import won't resolve. Pass the timeline via the inline workflow object above.

TSX
import { Composition, ProductFilm } from "@reshot/compose";

const capturePath = "./login-capture.mp4";

const workflow = {
  durationMs: 9203,
  capturePath,
  captureSize: { width: 1440, height: 900 },
  timeline: [
    { type: "queue_visible", tMs: 0 },
    { type: "hero_approve", tMs: 3000 },
    { type: "workflow_end", tMs: 9203 },
  ],
  targets: {
    version_history: { x: 1060, y: 381, w: 347, h: 125 },
  },
};

export default function LoginHero() {
  return (
    <Composition workflow={workflow} slug="LoginHero" capturePath={capturePath}>
      <ProductFilm
        src={capturePath}
        steps={[
          {
            id: "review-history",
            at: "queue_visible",
            until: "hero_approve",
            target: "version_history",
            label: "Version history stays in context",
          },
        ]}
      />
    </Composition>
  );
}

Each step anchors a callout to a target rect between two timeline events (atuntil); tone: "success" | "warning" colors it.

Render → push → review → deliver

Terminal
# 1. Capture the workflow (writes the capture mp4 + <name>.metadata.json + a scaffold)
reshot record "Login hero"

# 2. Render locally to MP4 + WebM + poster
reshot compose LoginHero.compose.tsx
#   → LoginHero.composed.mp4 / .webm / .webp

# 3. Upload for review (lands as PENDING — nothing serves until a human approves)
reshot compose push LoginHero.compose.tsx --project <projectId>

After push, the render appears in the review queue next to your screenshots. Approve it and the durable URL flips to the new video.

Delivery URLs

URLServes
reshot.dev/public/c/<projectSlug>/<compositionSlug>/latest.mp4The current approved render (self-updating)
…/latest.webm, …/poster.webpWebM + poster of the approved render
…/v/<renderId>.mp4A version-pinned render that never changes
/public/c/<projectSlug>/<compositionSlug>An embeddable player page

Before the first approval these return 404 — an unapproved render never serves.

CI auto-approve

In a trusted pipeline you can skip manual review:

Terminal
reshot compose push LoginHero.compose.tsx --project <projectId> --auto-approve

--auto-approve promotes the render to live immediately. It consumes a review approval, so it honors your plan's approval allowance — on the Free plan, once you've used your included approvals the render is left PENDING for review instead of bypassing the limit.