RVE LogoReact Video EditorDOCS

Rendering Outside the Editor

Use the useRender hook to trigger video rendering from any React component without mounting the full editor

Rendering Outside the Editor

The useRender hook lets you trigger video rendering from any React component — without mounting the full <ReactVideoEditor>. This is useful when you want to render from a dashboard, batch queue, custom UI, or anywhere outside the editor.

It supports both cloud rendering (via your VideoRenderer) and browser rendering (via WebCodecs), and returns a promise-based API with full state tracking.

import { useRender } from '@reactvideoeditor/react-video-editor/hooks/use-render';

Quick Example

import { useRender } from '@reactvideoeditor/react-video-editor/hooks/use-render';

function RenderButton({ tracks, renderer }) {
  const { render, state, cancel } = useRender('my-composition', {
    tracks,
    width: 1920,
    height: 1080,
    fps: 30,
    renderer, // cloud rendering
  });

  const handleRender = async () => {
    try {
      const result = await render('1080p');
      console.log('Video ready:', result.url, result.size);
    } catch (err) {
      console.error('Render failed:', err);
    }
  };

  return (
    <div>
      <button onClick={handleRender} disabled={state.status === 'rendering'}>
        {state.status === 'rendering'
          ? `Rendering ${Math.round(state.progress * 100)}%`
          : 'Render Video'}
      </button>
      {state.status === 'rendering' && (
        <button onClick={cancel}>Cancel</button>
      )}
      {state.status === 'done' && (
        <a href={state.url} download>Download</a>
      )}
    </div>
  );
}

Browser Rendering

To render entirely in the browser without a server, use webRender: true instead of providing a renderer:

const { render, state } = useRender('my-composition', {
  tracks,
  width: 1920,
  height: 1080,
  fps: 30,
  webRender: true,
});

const result = await render('1080p');
// result.url — blob URL to the rendered MP4

Browser requirements

Browser rendering requires WebCodecs support (Chrome 94+ or Edge 94+). See Client-Side Rendering for browser compatibility details.


Options

OptionTypeRequiredDefaultDescription
tracksTrack[]YesThe tracks to render
widthnumberYesComposition width in pixels
heightnumberYesComposition height in pixels
fpsnumberYesFrames per second
rendererVideoRendererCloud renderer instance (e.g. HttpRenderer). Either renderer or webRender must be provided
webRenderbooleanfalseEnable browser-side rendering via WebCodecs
backgroundColorstring'white'Background color for the composition
baseUrlstringOptional base URL for asset resolution
pollingIntervalnumber1000Cloud render progress polling interval (ms)
initialDelaynumber0Delay before first progress poll (ms)
onExportStartfunctionCalled when rendering begins
onExportCompletefunctionCalled when rendering succeeds
onExportFailfunctionCalled when rendering fails

Return Value

The hook returns an object with four properties:

PropertyTypeDescription
render(resolution?: ExportResolution, exportScale?: number) => Promise<RenderResult>Start a render. Resolves with { url, size } on success. Defaults to '1080p'. Pass an optional exportScale to override the preset — see Exporting Larger than the Canvas
stateRenderStateCurrent render state (see below)
cancel() => voidCancel an in-progress render and reset to init
reset() => voidReset state back to init, cancelling any in-progress render

RenderState

The state is a discriminated union — TypeScript narrows the available fields based on status:

type RenderState =
  | { status: 'init' }
  | { status: 'invoking' }
  | { status: 'rendering'; renderId: string; progress: number }
  | { status: 'error'; renderId: string | null; error: Error }
  | { status: 'done'; url: string; size: number };

RenderResult

type RenderResult = { url: string; size: number };

Lifecycle Callbacks

The hook fires the same lifecycle callbacks as the editor's built-in rendering:

const { render } = useRender('my-composition', {
  tracks,
  width: 1920,
  height: 1080,
  fps: 30,
  renderer,
  onExportStart: ({ compositionId, durationInSeconds, renderType }) => {
    console.log('Render started', compositionId);
  },
  onExportComplete: ({ compositionId, url, sizeInBytes, durationInSeconds, renderType }) => {
    console.log('Render complete', url);
  },
  onExportFail: ({ compositionId, error, durationInSeconds, renderType }) => {
    console.error('Render failed', error);
  },
});

Cancellation and Cleanup

  • cancel() aborts any in-flight render and resets state to init
  • reset() does the same — cancels and resets
  • Unmounting the component automatically cancels any in-progress render and cleans up resources (blob URLs, OPFS assets)
const { render, cancel } = useRender('my-composition', { /* ... */ });

// Start a render
const promise = render('1080p');

// Cancel it mid-way
cancel();

// The promise will reject with an AbortError

Resolution

The render() function accepts an optional ExportResolution parameter:

ResolutionShort Edge
'720p'720px
'1080p'1080px (default)
'4k'2160px
'original'The composition size (scale 1, no scaling)

The actual pixel dimensions are calculated from the width and height you provide, scaled to match the target resolution.


Exporting Larger than the Canvas

By default the export size is chained to the resolution preset. If you want to edit on a small canvas but export at full source resolution, pass an optional exportScale multiplier as the second argument to render(). When supplied, it overrides the resolution preset and drives the render scale directly:

// Preview/edit at 1332×720, export at ~3840×2076
const result = await render('original', 2.88);
  • When exportScale is provided, it takes precedence over the resolution preset.
  • When omitted, behaviour is unchanged — the preset drives the scale as before, so existing callers are unaffected.
  • exportScale must be a finite, positive number. Invalid values (e.g. 0, negative, NaN, Infinity) throw synchronously.

Hosts that think in pixels can compute the multiplier from the target width themselves — e.g. targetWidth / compositionWidth.

Cloud rendering requires composition-driven sizing

The browser render path honours exportScale natively. For cloud/Lambda renders, the scale only takes effect if your Remotion composition sizes itself from inputProps (width/height × scale) via calculateMetadata. If your composition hardcodes its dimensions, the scale is applied on web but ignored on the cloud. A one-time console.warn is emitted when exportScale is used on a cloud render as a reminder.


Types

Both RenderState and RenderResult are exported from the hook and from the main types entrypoint:

import type { RenderState, RenderResult } from '@reactvideoeditor/react-video-editor/hooks/use-render';
// or
import type { RenderState, RenderResult } from '@reactvideoeditor/react-video-editor/types';