RVE LogoReact Video EditorDOCS
RVE SDK/Components/React Video Editor

React Video Editor

A modular, drop-in React video editor with sidebar panels, Remotion-powered player, renderer abstraction, autosave, theming, and pluggable media adaptors

The RVE Editor is a composable, drop-in editor shell that wires together the core building blocks of a video editing UI: a Remotion-powered player, a modular sidebar with overlay panels, timeline configuration, autosave hooks, theme management, and a renderer abstraction. You stay in control of data and backends via explicit props and adaptors.

Under the hood, RVE uses Remotion for rendering and playback, and exposes a renderer interface (e.g. HTTP/SSR or Lambda) so you can plug in your own backend. Media sourcing is handled by a flexible adaptor layer (e.g. Pexels), while overlays and timeline config remain app-controlled.

Editor vs Player-only

Use full Editor mode for an all-in-one editing UI (sidebar + editor + autosave). Switch to player-only mode when you just need an embeddable, fullscreen player with overlays and responsive sizing, for example on mobile or preview pages.

Key Features

  • Renderer abstraction: Bring your own backend via a simple VideoRenderer interface; ships with HttpRenderer.
  • Player-only mode: Fullscreen, responsive player without the editor UI for lightweight embeds.
  • Modular sidebar: Default sidebar with pluggable overlay panels; disable or replace with your own.
  • Autosave hooks: onSave callback delivers the full serialized EditorState on every save, plus visible autosave status.
  • Theming: Custom theme registry, default themes toggle, and external theme switching.
  • Media adaptors: Plug-in sources for video, images, audio, stickers, templates, and animations.
  • Timeline config: Control rows, zoom constraints, snapping behavior, and push-on-drag.
  • Responsive video sizing: Aspect ratio and explicit dimension controls for predictable layout.
  • Mobile-conscious: Player-only mode includes mobile viewport height handling.

Usage Example

Basic (Client-Side Rendering — Default)

import { ReactVideoEditor } from '@reactvideoeditor/react-video-editor/react-video-editor';
import '@reactvideoeditor/react-video-editor/styles.css';

export default function VideoEditorPage() {
  return (
    <div className="w-full h-screen">
      <ReactVideoEditor
        projectId="MyComposition"
        fps={30}
        disabledPanels={[]}
        defaultTheme="dark"
        showDefaultThemes={true}
      />
    </div>
  );
}

With Server Rendering (Optional)

import React from 'react';
import { HttpRenderer } from '@reactvideoeditor/react-video-editor/utils/http-renderer';
import { ReactVideoEditor } from '@reactvideoeditor/react-video-editor/react-video-editor';
import { createPexelsVideoAdaptor } from '@reactvideoeditor/react-video-editor/adaptors/pexels-video-adaptor';
import { createPexelsImageAdaptor } from '@reactvideoeditor/react-video-editor/adaptors/pexels-image-adaptor';
import '@reactvideoeditor/react-video-editor/styles.css';

export default function VideoEditorPage() {
  const PROJECT_ID = 'MyComposition';

  const availableThemes = [
    { id: 'rve', name: 'RVE', className: 'rve', color: '#3E8AF5' },
  ];

  const ssrRenderer = React.useMemo(
    () =>
      new HttpRenderer('/api/latest/ssr', {
        type: 'ssr',
        entryPoint: '/api/latest/ssr',
      }),
    []
  );

  return (
    <div className="w-full h-screen">
      <ReactVideoEditor
        projectId={PROJECT_ID}
        fps={30}
        customRenderer={ssrRenderer}
        enableWebRender={true}

        disabledPanels={[]}
        availableThemes={availableThemes}
        defaultTheme="dark"
        sidebarWidth="400px"
        sidebarIconWidth="57.6px"
        showIconTitles={false}
        adaptors={{
          video: [createPexelsVideoAdaptor('YOUR_PEXELS_API_KEY')],
          images: [createPexelsImageAdaptor('YOUR_PEXELS_API_KEY')],
        }}
        onThemeChange={(themeId) => console.log('Theme changed:', themeId)}
      />
    </div>
  );
}

Component Structure

ReactVideoEditor/
├── components/
│   ├── core/
│   │   ├── editor.tsx              # Main editor surface
│   │   └── video-player.tsx        # Remotion-based player
│   ├── shared/
│   │   └── default-sidebar.tsx     # Default sidebar with overlay panels
│   ├── autosave/
│   │   └── autosave-status.tsx     # Autosave UI indicator
│   ├── providers/
│   │   ├── react-video-editor-provider.tsx  # Top-level provider composition
│   │   └── editor-provider.tsx              # Core editor context wiring
│   └── ui/                     # Sidebar/tooltip/button primitives
├── contexts/
│   ├── editor-context.tsx
│   ├── renderer-context.tsx
│   ├── media-adaptor-context.tsx
│   └── sidebar-context.tsx
├── hooks/
│   ├── use-overlays.tsx
│   ├── use-render.ts
│   └── use-extended-theme-switcher.ts
├── utils/
│   └── http-renderer.ts           # HTTP renderer implementing VideoRenderer
├── types/
│   ├── renderer.ts                # VideoRenderer interface
│   ├── overlay-adaptors.ts        # Overlay adaptor interfaces
│   └── index.ts                   # OverlayType, Overlay, etc.
└── constants.ts                   # DEFAULT_OVERLAYS, colors, etc.

Props

The ReactVideoEditor component extends the editor provider props and adds UI-level controls. Required props are marked with *.

PropTypeDefaultDescription
Core
projectId*string-Composition/session ID used for renders and state
rendererVideoRenderer-Deprecated — use customRenderer instead
fpsnumber30Frames per second for playback and renders
editorStateEditorState-Initial editor state. Pass the object from onSave to restore a previous session
Autosave
autoSaveIntervalnumber10000Autosave interval in milliseconds
onSave(state: EditorState) => void-Called on every save (autosave or manual). Receives the full serialized editor state — use this to persist to your backend
Sidebar and Layout
showSidebarbooleantrueToggle the default sidebar
customSidebarReactNode-Provide your own sidebar; hides the default
sidebarLogoReactNode-Custom logo for the default sidebar
sidebarFooterTextstring-Footer text in the default sidebar
disabledPanelsOverlayType[]-Hide specific overlay panels
showIconTitlesbooleantrueShow/hide sidebar icon titles
sidebarWidthstring"16rem"CSS width var for content sidebar
sidebarIconWidthstring"3rem"CSS width var for icon rail
classNamestring-Applied to main content inset
Player / Mode
isPlayerOnlybooleanfalseFullscreen player without editor UI
isLoadingProjectbooleanfalseWhether the project from URL is still loading
Renderer & API
customRendererVideoRenderer-Optional cloud/server renderer (SSR, Lambda, or custom). Not required — the SDK uses client-side rendering by default
enableWebRenderbooleantrue if no customRendererEnable client-side video export via WebCodecs. Enabled by default when no customRenderer is provided
exportOptionsExportOptions{ availableResolutions: ['720p', '1080p', '4k'], defaultResolution: '1080p' }Configure the resolution presets shown in the export dialog. See Export Options
baseUrlstring-Optional API base for editor operations
Media Adaptors
adaptorsOverlayAdaptors-Pluggable sources for content (video, images, audio, text, stickers, templates, animations)
Timeline
initialRowsnumber5Initial number of timeline rows
maxRowsnumber8Maximum number of timeline rows
Zoom
zoomConstraintsobject{ min: 0.2, max: 10, step: 0.1, default: 1 }Zoom level constraints
Snapping
snappingConfigobject{ thresholdFrames: 1, enableVerticalSnapping: true }Timeline snapping configuration
Feature Flags
disableMobileLayoutbooleanfalseDisable mobile-specific layout
disableVideoKeyframesbooleanfalseDisable video keyframe functionality
enablePushOnDragbooleanfalseEnable push-on-drag timeline behavior
Video Dimensions
videoWidthnumber1280Video output width
videoHeightnumber720Video output height
Theming
availableThemesCustomTheme[]-List of custom themes
selectedThemestring | undefined-Active theme id
onThemeChange(themeId: string) => void-Theme change callback
showDefaultThemesbooleantrueShow/hide default themes
hideThemeTogglebooleanfalseHide theme toggle UI
defaultThemestring"dark"Default theme to use
Watermark
watermarkWatermarkConfigtrueWatermark on your videos. true for default RVE badge, { src: '...' } for custom image, false for none. See Watermark
Status UI
showAutosaveStatusbooleantrueShow autosave indicator

Notes

  • The SDK ships with client-side rendering enabled by default. You do not need to provide a renderer to get video export working.
  • The component internally manages a playerRef and wires it into the VideoPlayer. If you need direct player access, read it from the editor context or adapt the provider usage directly.
  • Pass a previously-saved EditorState via the editorState prop to seed the editor with initial content.
  • The HttpRenderer is a convenience implementation of VideoRenderer that expects REST endpoints at /render and /progress. It is only needed if you want server-side rendering.

Export Options

The exportOptions prop controls the resolution presets shown in the export dialog. When a user clicks "Export", they see a dialog to choose a resolution before rendering begins. The pixel dimensions are calculated automatically based on the current aspect ratio.

<ReactVideoEditor
  projectId="my-project"
  exportOptions={{
    availableResolutions: ['720p', '1080p', '4k'],
    defaultResolution: '1080p',
  }}
/>
PropertyTypeDefaultDescription
availableResolutions('720p' | '1080p' | '4k')[]['720p', '1080p', '4k']Resolution presets offered in the export dialog
defaultResolution'720p' | '1080p' | '4k''1080p'Resolution selected when the dialog opens

The resolutions map to the short edge of the output — for a 16:9 aspect ratio, 1080p produces 1920×1080, while for 9:16 it produces 608×1080. The dialog shows the calculated pixel dimensions for each option.

EditorState In, EditorState Out

The onSave callback emits an EditorState on every autosave tick and manual save. Pass that same object back via the editorState prop to restore a session — no destructuring or field mapping required.

import { ReactVideoEditor } from '@reactvideoeditor/react-video-editor/react-video-editor';
import type { EditorState } from '@reactvideoeditor/react-video-editor/types';

function Editor({ savedState }: { savedState?: EditorState }) {
  const handleSave = (state: EditorState) => {
    // Persist to your backend
    fetch('/api/projects/my-project', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(state),
    });
  };

  return (
    <ReactVideoEditor
      projectId="my-project"
      editorState={savedState}
      onSave={handleSave}
    />
  );
}

The editor uses timestamp-based conflict resolution to decide whether to restore from local IndexedDB or use the provided editorState:

  • editorState with savedAt — compared against the local autosave timestamp. Whichever is newer wins. Local changes made after the last server save are preserved; fresh server data takes priority over stale local state.
  • editorState without savedAt — IndexedDB is always restored if a local save exists. The prop is treated as a fallback for when no local state is available.
  • editorState omitted — falls back to IndexedDB or internal defaults.

The savedAt field is stamped automatically on every save and included in the onSave payload — persist it alongside the rest of the state and pass it back.

The EditorState type contains source-of-truth fields only (no derived values):

interface EditorState {
  tracks: Track[];
  aspectRatio: AspectRatio;    // '16:9' | '1:1' | '4:5' | '9:16'
  backgroundColor: string;
  playbackRate: number;
  savedAt?: number;            // Unix-ms timestamp, set automatically on every save
}

Need pixel dimensions for an aspect ratio? Use the exported getCanvasDimensions utility:

import { getCanvasDimensions } from '@reactvideoeditor/react-video-editor/types';

const { width, height } = getCanvasDimensions('16:9');
// → { width: 1920, height: 1080 }

OPFS asset URLs

Asset URLs from the browser's Origin Private File System (OPFS) are dehydrated to asset:{id} references in the state. If your workflow uses external URLs (e.g. https://), they pass through as-is. OPFS-based assets will need a URL resolution strategy on your backend — this is a separate concern from state persistence.

Deprecated: onSaving / onSaved

The onSaving and onSaved props have been removed. Use onSave instead — it provides the full EditorState on every save, replacing both callbacks with a single, more useful one.

Next Steps

  • Get started with the default sidebar panels and overlays — client-side export works out of the box.
  • Optionally wire up a server renderer (SSR or Lambda) via customRenderer for production workloads.
  • Add media sources via adaptors (e.g. Pexels for images/videos).
  • Customize themes and integrate your app's theme switching UX.