How to Keep Browser, Server, and Editor in Sync With One VideoJSON File

If you are building programmatic video tools, the hard part is usually not generating one render. It is keeping preview, export, and editing on the same project format. VideoFlow is built for that problem: you author a video in TypeScript, compile it into portable VideoJSON, and render the same scene in the browser, on a server, or inside a live DOM preview.

That is the thread running through How to Build a Browser-Based MP4 Export Workflow With VideoFlow, How to Build a Reviewable JSON-to-Video Pipeline With VideoFlow, How to Build a Merge-Request-Friendly Video Workflow With VideoFlow, and How I Build a Three-Renderer Video Workflow With VideoFlow. This guide narrows the problem to one rule: keep one JSON source of truth and make every renderer consume it.

The relevant docs are the VideoFlow docs, the renderer docs, and the React video editor docs.

1. Start With One VideoJSON Contract

Use @videoflow/core as the authoring layer. Keep the project definition in one TypeScript file and compile it into VideoJSON instead of hand-building separate browser and server timelines.

npm install @videoflow/core

import VideoFlow from "@videoflow/core";

const $ = new VideoFlow({
  name: "Launch Preview",
  width: 1920,
  height: 1080,
  fps: 30,
});

$.addText(
  { text: "Hello, VideoFlow!", fontSize: 7, fontWeight: 800 },
  { transitionIn: { transition: "overshootPop", duration: "500ms" } }
);

const json = await $.compile();
const blob = await $.renderVideo();

Expected result: one JSON artifact becomes the handoff between authoring, preview, and export.

Abstract pipeline diagram linking one video file to three render targets

2. Pick the Right Renderer for the Job

VideoFlow’s renderer split is the part that makes the workflow portable. The same VideoJSON can drive browser export, server-side rendering, and a live DOM preview, so you can keep the project format stable while choosing the runtime that fits the job.

Use the browser renderer when the user is already inside your app and clicks Export. Use the server renderer when you need batch jobs, queue workers, scheduled renders, or API-driven jobs. Use the DOM renderer when you need a live, scrubbable preview that tracks the project as it changes.

A practical rule:

  • Browser renderer for low-friction, client-side export.
  • Server renderer for queues, APIs, and large jobs.
  • DOM renderer for editors, dashboards, and frame-accurate preview.

Expected result: you stop rewriting timelines for each runtime and start choosing a renderer by operational need instead of by project format.

One VideoJSON source branching to browser preview, server render, and editor

3. Add a React Editor Without Changing the Format

If users need to edit the video inside your product, add the optional React editor rather than inventing a second data model. The editor accepts the same project JSON, gives you a multi-track timeline, and keeps the preview tied to the same underlying structure.

import { VideoEditor } from "@videoflow/react-video-editor";
import "@videoflow/react-video-editor/style.css";

export default function App() {
  return (
    <VideoEditor
      video={videoJSON}
      onChange={(next) => saveToServer(next)}
      onSave={async (next) => await persist(next)}
      onUpload={async (file) => await upload(file)}
      theme="dark"
    />
  );
}

The editor path is where teams usually drift. The safe pattern is to treat the editor as a view over VideoJSON, not as a competing source of truth. That keeps layers, keyframes, transitions, themes, and MP4 export aligned with the core project.

Expected result: users can trim, reorder, keyframe, and export without creating a second format that later needs migration.

Video editor timeline and inspector for a JSON-first project

4. Keep the Project Reviewable in Git

This is where VideoFlow becomes useful for teams that care about code review. When the project lives in Git, the rendered output is still important, but the reviewable artifact is the project JSON or the TypeScript builder that creates it.

Keep one file per template. Review changes in a branch before they reach production. Re-render the same JSON after merge so the visual output stays tied to a commit instead of to a manual editing session.

That is why the Git-focused posts in this series matter: How to Build a Git-Friendly Video Template System With VideoFlow shows the template side, while How to Build a Reviewable JSON-to-Video Pipeline With VideoFlow and How to Build a Merge-Request-Friendly Video Workflow With VideoFlow show how to make that reviewable in practice.

Expected result: diffs are small enough to inspect, and the same project can move cleanly from branch to render without format drift.

Git-friendly diff view for a VideoFlow project

5. Decide Which Path Owns Export

Once the shared format is in place, the decision becomes operational rather than architectural.

If the user is exporting inside your app, let the browser renderer own that path. If the job is queued, scheduled, or triggered by automation, let the server renderer own it. If the user needs to edit the content before export, keep the React editor in front of the same VideoJSON and let the renderer consume the result.

That is the same pattern behind How to Build a Portable Video Export Workflow With VideoFlow and How to Build a Browser-Based MP4 Export Workflow With VideoFlow. The format stays still even when the runtime changes.

Conclusion

The simplest way to keep browser preview, server export, and editor behavior in sync is to stop letting each one invent its own project shape. Build once in @videoflow/core, compile to VideoJSON, and let the browser, server, and editor all consume the same artifact.

If you want to try it, start with the VideoFlow Playground, install @videoflow/core, and render one small scene in both the browser and server paths before you expand the template.