Skip to content

Editor quickstart

@ilovevideoeditor/react-video-editor is a drop-in editor UI — timeline, preview, inspector, keyboard shortcuts, undo/redo. You feed it a VideoJSON and it hands back a new one on every change.

Minimal example

jsx
import { VideoEditor } from '@ilovevideoeditor/react-video-editor';
import '@ilovevideoeditor/react-video-editor/style.css';

const initialVideo = {
  name: 'My project',
  width: 1920, height: 1080, fps: 30, duration: 10,
  layers: [],
};

export default function App() {
  return (
    <VideoEditor
      video={initialVideo}
      onChange={(next) => console.log('edited', next)}
      onSave={(video) => saveToServer(video)}
      onExportComplete={(blob) => downloadBlob(blob, 'out.mp4')}
      theme="dark"
    />
  );
}

NOTE

Export. Most apps should leave onExport undefined and rely on the built-in export modal + onExportComplete to receive the rendered Blob. Set onExport only when you want to skip the modal and run your own UI / pipeline; whatever it resolves to is forwarded to onExportComplete.

Props

PropTypeNotes
videoVideoJSONInitial state. Controlled re-renders supported.
onChange(video) => voidFires on every edit. Debounced internally.
onSave`(video) => voidPromise<void>`
onExport() => Promise<unknown>Optional custom export. When set, replaces the built-in modal; whatever it resolves to is forwarded to onExportComplete.
onExportComplete(video) => voidReceives the rendered Blob from the built-in modal (or the value onExport resolved to). When set with the built-in modal, the editor skips its auto-download.
onUpload(file) => Promise<string>Resolve to a URL. See Uploads.
theme`'light''grey'
branding`ReactNodenull
components{ Titlebar, Preview, Sidebar, Timeline }Swap any panel. See Custom panels.

Sizing

The editor fills its parent. Wrap it in a block with an explicit height — typically 100vh minus your app chrome.

jsx
<section style={{ height: 'calc(100vh - 64px)' }}>
  <VideoEditor video={v} onChange={setV} theme="dark" />
</section>

NOTE

SSR. The editor is client-only. Dynamically import it in Next / Remix / React Router — or guard with a useEffect.

Released under the MIT License.