Skip to main content

Exporting

exportVideoComposition renders a composition to an H.264 file. It runs on a dedicated worklet thread: a synchronous extractor decodes the exact frame for each timestamp, your drawFrame renders it into an offscreen Skia surface, and the surface's texture is handed straight to the hardware encoder (MediaCodec on Android, AVAssetWriter on iOS) — no pixel readback. Audio items are mixed and encoded alongside.

import { exportVideoComposition } from '@azzapp/react-native-skia-video';

await exportVideoComposition({
videoComposition: composition,
drawFrame, // the SAME worklet as the preview
outPath: '/path/to/output.mp4',
width: 1080,
height: 1920,
frameRate: 30,
bitRate: 8_000_000,
onProgress: ({ framesCompleted, nbFrames }) =>
setProgress(framesCompleted / nbFrames),
});

Options

OptionTypeDescription
videoCompositionVideoCompositionThe composition to render.
drawFrameFrameDrawerWorklet drawing each frame.
outPathstringDestination file path (no file:// scheme).
width / heightnumberOutput resolution in pixels.
frameRatenumberFrames per second.
bitRatenumberVideo bit rate in bits per second.
encoderNamestringAndroid only: a specific encoder from getValidEncoderConfigurations.
audioBitRate / audioSampleRate / audioChannelCountnumberAAC settings when the composition has audio.
onProgress({ framesCompleted, nbFrames }) => voidCalled after each encoded frame.
abortSignalAbortSignalCancels the export; the promise rejects with an AbortError.
beforeDrawFrame / afterDrawFramefunctionsPer-frame context hooks, as in playback.

Cancellation

const controller = new AbortController();
exportVideoComposition({ ...options, abortSignal: controller.signal }).catch(
(e) => {
if (e.name === 'AbortError') return; // user cancelled
throw e;
}
);
// later…
controller.abort();

Good practices

  • Reuse the preview's drawFrame. That's the whole point: pass the same worklet (with the same state snapshot) and the exported file matches the preview pixel for pixel.
  • Prime per-runtime caches. The export runs on its own worklet runtime: Skia objects cached for the preview (UI runtime) are not visible there. Capture raw bytes in the export closure and decode them on first frame, or rebuild caches lazily.
  • Negotiate the configuration on Android. Encoders differ wildly between devices; feed your target settings through getValidEncoderConfigurations and use the first result.
  • Cap decode resolutions. Item resolution also applies during export; decoding 4K sources to render 1080p output wastes decoder sessions (some devices only have a few).