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, text, templates, Lottie, animations, and transcription.
  • Timeline config: Control zoom constraints, snapping behavior, and push-on-drag.
  • Shapes, keyframes, and templates: See Shapes, Keyframes, and Templates.
  • 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>
  );
}

Package Entry Points

You can import only from the entry points that the package exports. Internal source files are not part of the public API.

Entry pointContents
/react-video-editorThe ReactVideoEditor component
/styles.cssThe compiled editor stylesheet
/typesPublic types (EditorState, Track, Item, adaptor types, export types) and helpers such as getCanvasDimensions, estimateExportSize, and PAPER_PRESETS
/constantsEditor constants
/hooks/use-renderThe standalone useRender hook
/hooks/use-project-state-from-url, /hooks/use-alignment-guidesHelper hooks
/utils/http-rendererHttpRenderer, a VideoRenderer for HTTP render endpoints
/utils/remotion/aws-lambda/deployThe Lambda deploy helper
/adaptors/pexels-video-adaptor, /adaptors/pexels-image-adaptor, /adaptors/whisper-transcription-adaptorAdaptor factories
/remotion/components/*-layer-content, /remotion/layer-box, /remotion/layer-z-indexBuilding blocks for a custom composition
/data/google-fonts, /data/google-fonts-listThe Google Fonts data
/mobile-warning-modal, /project-load-confirm-modalStandalone dialogs

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
disabledPanelsstring[][]Hide sidebar panels by id. See the panel ids
showIconTitlesbooleantrueShow/hide sidebar icon titles
sidebarWidthstring"256px" ("400px" with screenRecordingTools)CSS width of the content panel
sidebarIconWidthstring"48px"CSS width of the icon rail
classNamestring-Applied to main content inset
screenRecordingToolsbooleanfalseAdd and open the beta screen-recording Home panel for backgrounds, screen framing, camera layouts, borders, cursor controls, and automatic zoom. See Screen Recorder mode
Player / Mode
isPlayerOnlybooleanfalseFullscreen player without editor UI
isLoadingProjectbooleanfalseWhether the project from URL is still loading
timelineHeaderConfigTimelineHeaderConfigMode defaultsShow or hide the aspect-ratio and playback-speed controls. Screen Recorder mode hides both controls by default
enableCanvasZoombooleantrueEnable wheel zoom, wheel pan, and drag pan on the editor canvas. This does not change video zoom effects
showAlignmentGuidesbooleantrueShow alignment guides when you move items on the canvas
showBuiltWithRvebooleanfalseShow a "Built with RVE" link badge on the player
showBrowserWarningbooleantrueShow a dialog when the browser does not support features such as video export and audio processing
browserWarningMessageReactNode-Custom content for the browser warning dialog
showMobileWarningbooleantrueShow a one-time dialog on mobile screens. The dialog tells the user that the editor works best on a desktop
Renderer & API
customRendererVideoRenderer-Optional cloud/server renderer (SSR, Lambda, or custom). Not required — the SDK uses client-side rendering by default. If the renderer is enabled, the editor starts in cloud mode
enableWebRenderbooleantrue if no customRendererEnable client-side video export via WebCodecs. Enabled by default when no customRenderer is provided
webRenderboolean-Deprecated — use enableWebRender instead
exportOptionsExportOptions{ availableResolutions: ['720p', '1080p', '4k'], defaultResolution: '1080p', quality: 'high' }Configure the resolution presets in the export dialog and the quality of browser exports. See Export Options
disableExportModalbooleanfalseSkip the resolution dialog. The Render button exports immediately at the exact composition size. See Composition Dimensions
defaultExportScalenumber-Render multiplier applied by the built-in Render button, so you can edit on a small logical canvas but export at native resolution (composition size × scale). Only applies on the disableExportModal immediate-render path. See Composition Dimensions
baseUrlstring-Optional API base for editor operations
Export Lifecycle
onExportStart(params: ExportStartParams) => void-Called when an export starts. For browser exports, params.estimatedSizeInBytes gives the estimated file size
onExportProgress(params: ExportProgressParams) => void-Called for progress changes and periodic heartbeats until the export ends
onExportComplete(params: ExportCompleteParams) => void-Called when an export completes
onExportFail(params: ExportFailParams) => void-Called when an export fails or times out
onExportCancel(params: ExportCancelParams) => void-Called when the user cancels an export. A cancellation does not call onExportFail
exportProgressIntervalMsnumber1000Time in milliseconds between progress heartbeats. Set a value of 0 or less to stop heartbeats
webRenderStallTimeoutMsnumber300000Maximum visible-tab time in milliseconds without browser render progress. Set 0 to stop this timeout
webRenderFinalizationTimeoutMsnumber1800000Maximum visible-tab time in milliseconds for each browser finalization phase
Media Adaptors
adaptorsAdaptors-Pluggable sources for content: video, images, audio, text, templates, animations, lottie, transcription, and localMedia. If you omit a media type, the editor keeps its default stock adaptors for that type. If you set templates or lottie, your array replaces the built-in adaptors
mediaLibraryMediaLibraryConfig-Configure the in-panel media library tabs (Stock / My Library): which tabs show, their order, and their labels. Omit to derive tabs from configured adaptors. See Configuring the library tabs
featureOverridesFeaturesConfig-Add badges, upgrade prompts, or usage tracking to item actions. See Feature Overrides
Timeline
thumbnailIntervalnumberAdaptiveSeconds between generated timeline thumbnails. Smaller values add detail but use more processing and cache
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
disableVideoKeyframesbooleanfalseHas no effect in the current version. The editor keeps the prop for compatibility
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
watermarkWatermarkConfigfalseWatermark on the player and on exports. true for the default RVE badge, { src: '...' } for a 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.
  • The timeline opens with 4 track rows or fewer. If a project has more tracks, the tracks scroll inside the timeline panel. You can drag the panel taller.

Timeline Thumbnails

Configure generated thumbnails

Use thumbnailInterval to set the number of seconds between generated timeline thumbnails. A smaller value gives a more detailed timeline and uses more processing and cache space. If you omit the prop, the editor chooses an adaptive interval based on the timeline zoom and clip length.

<ReactVideoEditor projectId="my-project" thumbnailInterval={10} />

Changing the prop causes the editor to generate and cache a sprite for the new interval. An invalid value, 0, or NaN restores adaptive interval selection.

Use external preview thumbnails

Set previewThumbnails on a video item—not on ReactVideoEditor—when your video service supplies a thumbnail sprite and a WebVTT file. VTT cues must use #xywh=x,y,width,height Media Fragment rectangles.

const videoItem: VideoItem = {
  src: 'https://video.gumlet.io/.../video.mp4',
  previewThumbnails: {
    sprite: 'https://video.gumlet.io/.../preview_thumbnails.png',
    vtt: 'https://video.gumlet.io/.../preview_thumbnails.vtt',
  },
};

The timeline fits the supplied sprite thumbnails to its cells and ignores cues outside the sprite image. It uses the supplied files without downloading the video to generate thumbnails.

No generated fallback

If the VTT cannot load or has no valid cues, the timeline shows its thumbnail error state. It does not download the video to generate thumbnails as a fallback.

Export Lifecycle

Use the export callbacks to show status in your host application or to monitor an export.

<ReactVideoEditor
  projectId="my-project"
  onExportProgress={({ phase, progress, heartbeat, renderedFrames, encodedFrames }) => {
    console.log({ phase, progress, heartbeat, renderedFrames, encodedFrames });
  }}
  onExportCancel={({ compositionId }) => {
    console.log('Export cancelled', compositionId);
  }}
  onExportFail={({ error }) => {
    console.error('Export failed', error);
  }}
/>

onExportProgress receives these fields:

FieldTypeDescription
phase'preparing' | 'rendering' | 'muxing' | 'storing'Current export phase. The UI shows muxing and storing as finishing
progressnumberExport progress from 0 to 1
renderedFramesnumber | undefinedNumber of rendered frames. Available for browser exports
encodedFramesnumber | undefinedNumber of encoded frames. Available for browser exports
totalFramesnumberTotal number of frames in the export
timestampnumberUnix timestamp in milliseconds for this update
heartbeatbooleantrue when the update has no new renderer progress
compositionIdstringProject or composition ID
durationInSecondsnumberExport duration in seconds
renderType'ssr' | 'lambda' | 'browser' | 'custom'Active render type

Browser exports have progress and finalization watchdogs. The progress watchdog restarts when the renderer moves forward. Each of the muxing and storing phases gets a separate finalization deadline. The watchdog deadlines pause when the document is hidden.

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. The same prop also sets the quality of browser exports.

<ReactVideoEditor
  projectId="my-project"
  exportOptions={{
    availableResolutions: ['720p', '1080p', '4k'],
    defaultResolution: '1080p',
    quality: 'high',
  }}
/>
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
quality'very-low' | 'low' | 'medium' | 'high' | 'very-high''high'Bitrate preset for browser exports. A higher preset gives a larger file
videoBitratenumber-Video bitrate in bits per second. Overrides quality for the video track
audioBitratenumber-Audio bitrate in bits per second. Overrides quality for the audio track

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.

Export Quality and File Size

The quality, videoBitrate, and audioBitrate options apply to browser exports only. Cloud renders (SSR, Lambda, or RVE Cloud Rendering) use their own bitrate.

The default preset is 'high'. It encodes 1080p video at 6 Mbps. Earlier versions always used 'very-high' (12 Mbps), so the files were twice as large. To keep the earlier file size, set quality: 'very-high'.

qualityVideo at 720pVideo at 1080pVideo at 4KAudio
'very-low'~0.4 Mbps0.9 Mbps~3.4 Mbps96 kbps
'low'~0.8 Mbps1.8 Mbps~6.7 Mbps96 kbps
'medium'~1.4 Mbps3 Mbps~11 Mbps128 kbps
'high'~2.8 Mbps6 Mbps~22 Mbps192 kbps
'very-high'~5.6 Mbps12 Mbps~45 Mbps192 kbps

For an exact bitrate, set videoBitrate and audioBitrate in bits per second:

<ReactVideoEditor
  projectId="my-project"
  exportOptions={{ videoBitrate: 2_500_000, audioBitrate: 128_000 }}
/>

The export dialog footer shows the estimated file size for the selected resolution. onExportStart gives the estimate as estimatedSizeInBytes, and onExportComplete gives the real sizeInBytes. The encoder uses a variable bitrate. Thus simple content, such as slides or screen recordings, is usually smaller than the estimate.

If the quality value is not valid, or a bitrate is not a positive number, the export fails before it starts.

To show the estimate in your own UI, use estimateExportSize. Give it the encoded size of the export, not the canvas size:

import {
  estimateExportSize,
  getEncodedExportDimensions,
} from '@reactvideoeditor/react-video-editor/types';

const { width, height } = getEncodedExportDimensions({ width: 1280, height: 720 }, '1080p');
const bytes = estimateExportSize({ width, height, durationInSeconds: 60, quality: 'medium' });

The /types entry point also exports these helpers and types:

  • resolveExportBitrates(width, height, options) returns the { videoBitrate, audioBitrate } that a browser export of that size uses.
  • DEFAULT_EXPORT_QUALITY is 'high'.
  • The types are ExportOptions, ExportResolution, ExportQuality, ExportBitrateOptions, ExportBitrates, and EstimateExportSizeParams.

Composition Dimensions

Set the canvas to an exact pixel size at any ratio via editorState.dimensions. The same size is used in both preview and export.

<ReactVideoEditor
  projectId="my-project"
  editorState={{ tracks, dimensions: { width: 593, height: 4395 } }}
  disableExportModal
/>

How it works:

  • Size is the source of truth. You pass dimensions; the aspect-ratio label is derived from it. The ratio dropdown shows a matching preset (16:9, 1:1, …) when one fits, otherwise the raw dimensions (e.g. 594 × 4396). Picking a preset just sets a new size and reframes items.
  • Omit dimensions and the canvas defaults to 16:9.
  • Export at the exact size. With a custom size, the export dialog adds an "Original" option (the composition size) and selects it by default. It's hidden when a preset already produces that size, so standard sizes like 1920×1080 don't show a duplicate row.
  • disableExportModal skips the dialog entirely — the Render button exports immediately at "Original" (the composition size), no scaling. Use it when the canvas size is the output size.
  • defaultExportScale lets that immediate-render path export larger than the canvas. The Render button renders at composition size × the scale, so you can edit on a small logical canvas but export at native resolution (e.g. preview 1332×720 with defaultExportScale={2.88} gives ~3840×2076). It only applies together with disableExportModal — the resolution dialog has its own presets and ignores it, so setting the prop without disableExportModal is a no-op.
  • Persistence. dimensions is saved in EditorState, so custom sizes survive reload. Pass the object back to restore.
<ReactVideoEditor
  projectId="my-project"
  editorState={{ tracks, dimensions: { width: 1332, height: 720 } }}
  disableExportModal
  defaultExportScale={2.88}
/>

SSR / Lambda renders

For cloud rendering, your app's Remotion <Composition> must read width/height from inputProps for arbitrary sizes to reach the render. Client-side (WebCodecs) export uses the composition size directly.

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[];             // See Tracks and Items
  dimensions?: { width: number; height: number }; // Canvas size at any ratio. Omit for the 16:9 default (1280×720). See Composition Dimensions
  aspectRatio?: AspectRatio;   // Deprecated — prefer `dimensions`. '16:9' | '1:1' | '4:5' | '9:16' | 'custom'
  background?: CanvasBackground; // Composition background. It is not a timeline item
  backgroundColor?: string;    // Deprecated — prefer `background`. Saved output still includes it
  playbackRate: number;
  savedAt?: number;            // Unix-ms timestamp, set automatically on every save
}

type CanvasBackground =
  | { type: 'color'; value: string }
  | { type: 'gradient'; value: string }
  | { type: 'image'; src: string; fit: 'cover' | 'contain'; position: string; blur: number; wallpaperId?: string }
  | { type: 'css'; value: string }; // Keeps legacy CSS backgrounds

For the structure of tracks, see Tracks and Items.

aspectRatio is deprecated

aspectRatio is now optional and deprecated in favor of dimensions. State that sets only aspectRatio still works — the size is seeded from the preset — so no changes are required for existing consumers. See Composition Dimensions.

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');
// Result: { 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.
  • Connect RVE Cloud Rendering through customRenderer for production workloads.
  • Use self-managed SSR or Lambda only when your team must own the rendering infrastructure.
  • Add media sources via adaptors (e.g. Pexels for images/videos).
  • Learn the editing tools and shortcuts in Editing and Shortcuts.
  • Customize themes and integrate your app's theme switching UX.