@zlucksolutions/react-native-video-editor
v1.0.7
Published
Video Editor tool for react-native(Trim, crop etc., add music, add textoverlay etc.)
Maintainers
Readme
react-native-video-editor
A UI-based, native video editing SDK for React Native.
This library provides a ready-to-use video editor UI (similar to social media editors) that allows users to trim, crop, add music, text overlays, and voice-overs — all powered by native iOS and Android implementations.
Important: App developers do not need to construct JSON editing configurations. You simply open the editor, enable features via boolean flags, and receive the exported video.
SDK Demo
Features
- Built-in video editor UI
- Trim video
- Crop / change aspect ratio
- Add background music (BGM)
- Add text overlays
- Add voice-over
- Automatic audio ducking
- Native export to device storage
- Event-driven editor lifecycle
Installation
1. Install the library
npm install @zlucksolutions/react-native-video-editoror
yarn add @zlucksolutions/react-native-video-editor2. Install peer dependencies (Automated) ⚡
Run this single command to install all required peer dependencies:
npx react-native-video-editor installThis will automatically:
- ✅ Detect your package manager (npm/yarn/pnpm)
- ✅ Install all 16 required peer dependencies
- ✅ Apply patches to peer dependencies
- ✅ Run
pod installfor iOS - ✅ Display setup instructions
2.1. Enable automatic patch application
To ensure patches are applied whenever you install dependencies, add the following to your project's package.json:
{
"scripts": {
"postinstall": "patch-package"
},
"devDependencies": {
"patch-package": "^8.0.1"
}
}Then install patch-package:
npm install --save-dev patch-packageor
yarn add -D patch-packageWhy is this needed? This package includes patches for some peer dependencies (like
react-native-audio-recorder-player) to fix known issues. Thepostinstallscript ensures these patches are automatically applied after everynpm installoryarn install.
2.2. Manual Patching (Alternative)
If you don't want to use patch-package automatically, you can apply the patch manually:
Locate the patch file in your project:
node_modules/@zlucksolutions/react-native-video-editor/patches/react-native-audio-recorder-player+3.6.14.patchCopy it to your project root
patchesfolder:mkdir -p patches cp node_modules/@zlucksolutions/react-native-video-editor/patches/react-native-audio-recorder-player+3.6.14.patch patches/Apply it using
patch:patch -p1 < patches/react-native-audio-recorder-player+3.6.14.patch
You can also view/download the patch file directly from the repository: Download Patch
If you prefer to install dependencies manually:
npm install \
@gorhom/bottom-sheet \
@react-native-documents/picker \
react-native-create-thumbnail \
react-native-fast-image \
react-native-fs \
react-native-gesture-handler \
react-native-linear-gradient \
react-native-nitro-modules \
react-native-nitro-sound \
react-native-permissions \
react-native-reanimated \
react-native-safe-area-context \
react-native-size-matters \
react-native-video \
react-native-worklets \
react-native-audio-recorder-playerThen run:
cd ios && pod install3. Permissions setup
Android
Add these permissions to your android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />RECORD_AUDIOis required for the voice-over feature. The SDK requests it at runtime when the user opens the recorder.WRITE_EXTERNAL_STORAGEis only needed to save the exported video to the public Downloads folder on API 32 and below. On API 33+ the SDK writes there without it, so the permission is capped withandroid:maxSdkVersion="32"— declaring it unbounded will be flagged by Play Console and by lint.
Note: you do not need
READ_EXTERNAL_STORAGE. Videos and background music are picked through the Storage Access Framework (@react-native-documents/picker), which grants per-file access without a storage permission.
iOS
Add this usage description to your ios/YourProjectName/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>We need access to the microphone to record audio for your videos.</string>That is the only key the SDK itself requires — the exported video is written to
the app's own Documents/Downloads directory, so no Photos permission is
needed to save it.
If your app picks source videos from the photo library (the SDK does not; it only receives a URI you pass in), add the relevant key for that yourself:
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to select videos</string>4. iOS setup (CocoaPods)
cd ios
pod install5. Wrap your app with GestureHandlerRootView
This step is mandatory.
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
{/* Your app root */}
</GestureHandlerRootView>
);
}6. Enable react-native-worklets Babel plugin
Update your babel.config.js:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-worklets/plugin'],
};Restart Metro after updating Babel config:
npx react-native start --reset-cacheUsage
Imports
import {
openVideoEditor,
VideoEditorHost,
} from '@zlucksolutions/react-native-video-editor';Opening the Video Editor
const result = await openVideoEditor({
source: videoUri,
editTrim: true,
editCrop: true,
editBGM: true,
editTextOverlay: true,
editVoiceOver: true,
});Options
| Option | Type | Default | Description |
| ------------------------- | ------------------------------- | ----------- | -------------------------------------- |
| source | string | required | URI of the video to edit |
| editTrim | boolean | false | Show the trim tool |
| editCrop | boolean | false | Show the crop / aspect-ratio tool |
| editBGM | boolean | false | Show the background-music tool |
| editTextOverlay | boolean | false | Show the text-overlay tool |
| editVoiceOver | boolean | false | Show the voice-over recorder |
| fontFamily | string | system font | Font used for text overlays |
| isVisionCamera | boolean | false | Source was recorded with Vision Camera |
| isGesture | boolean | false | Device uses gesture navigation |
| preserveSessionOnExport | boolean | false | Keep the editor open after export |
| onExportNext | (exportedUri: string) => void | none | Receives the exported URI |
| disableResetOnUnmount | boolean | false | Keep the session when editor unmounts |
source accepts a file:// path, an Android content:// URI, or a remote
http(s) URL.
fontFamily takes a font filename without its extension - see
Custom Font Support.
isVisionCamera - set this to true when source came from
react-native-vision-camera. Those recordings carry orientation metadata that
must be corrected during export; without this flag the exported video can come
out rotated.
isGesture - set this to true on devices using gesture navigation so the
bottom sheets leave room for the gesture bar instead of being clipped by it.
preserveSessionOnExport, onExportNext and disableResetOnUnmount
change how you receive the exported video - see Export modes.
Result
{
success: boolean;
exportedUri?: string;
error?: string;
}Export modes
The editor exports in one of two ways, and this changes how you get the result back.
Default — the promise resolves
By default, the user taps Next, the editor closes, and the promise returned by
openVideoEditor resolves with the exported URI:
const result = await openVideoEditor({ source: videoUri, editTrim: true });
// result.exportedUriThe Save action is separate: it writes the video into the device's Downloads folder and leaves the editor open. It does not resolve the promise.
Keep the editor open — preserveSessionOnExport
If you want to hand the exported video to your own screen (an upload flow, a
preview, a caption step) without tearing the editor down, pass both
preserveSessionOnExport and onExportNext:
openVideoEditor({
source: videoUri,
editTrim: true,
preserveSessionOnExport: true,
disableResetOnUnmount: true,
onExportNext: (exportedUri) => {
// Editor stays mounted with all edits intact.
navigation.navigate('Upload', { uri: exportedUri });
},
});Important: in this mode
onExportNextis called instead of closing the editor, so theopenVideoEditorpromise never resolves. Do notawaitit — take the result fromonExportNext. AdddisableResetOnUnmount: trueif the user should be able to come back and find their edits still there.
Custom Font Support
To use custom fonts for text overlays, follow the steps below.
Step 1: Download the Font
Visit Google Fonts, choose your desired font, and download the zip file.
Step 2: Add Fonts to Your Project
Create a fonts folder in your project (recommended path: src/assets/fonts/) and place your .ttf files inside it.
Step 3: Add Config File
Create a react-native.config.js file at the root of your project and add:
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./src/assets/fonts'], // adjust path if needed
};Step 4: Link the Fonts
Run the following command at the root of your project:
npx react-native-assetThis will automatically copy the fonts into the correct native folders for both iOS and Android.
Step 5: Use the Font
Pass the font filename (without extension) as the fontFamily prop:
openVideoEditor({
source: videoUri,
fontFamily: 'LobsterRegular', // filename without .ttf
});Minimal Example
import { View, Button, Alert } from 'react-native';
import {
openVideoEditor,
VideoEditorHost,
} from '@zlucksolutions/react-native-video-editor';
export default function Example() {
const openEditor = async () => {
const result = await openVideoEditor({
source: 'file:///path/to/video.mp4',
editTrim: true,
editCrop: true,
editBGM: true,
editTextOverlay: true,
editVoiceOver: true,
});
if (result.success && result.exportedUri) {
Alert.alert('Video exported', result.exportedUri);
} else {
Alert.alert('Editor failed', result.error ?? 'Unknown error');
}
};
return (
<View style={{ flex: 1 }}>
<Button title="Open Video Editor" onPress={openEditor} />
{/* REQUIRED: mount editor host once */}
<VideoEditorHost />
</View>
);
}VideoEditorHost (Required)
VideoEditorHost must be rendered once in your app (preferably near the root). It is responsible for rendering the native editor UI and managing its lifecycle.
<VideoEditorHost />openVideoEditor works by signalling this host, so calling it without a mounted
VideoEditorHost does nothing and the promise never resolves.
VideoEditorSDK (Advanced)
Most apps should use openVideoEditor + VideoEditorHost. If you would rather
render the editor yourself - as its own screen or route, instead of an imperative
call - mount VideoEditorSDK directly. It takes every option from the table
above, plus a required onCloseEditor callback that replaces the promise:
import { VideoEditorSDK } from '@zlucksolutions/react-native-video-editor';
export default function EditorScreen({ route, navigation }) {
return (
<VideoEditorSDK
source={route.params.videoUri}
editTrim
editCrop
editBGM
editTextOverlay
editVoiceOver
onCloseEditor={({ success, exportedUri, error }) => {
if (success && exportedUri) {
navigation.navigate('Preview', { uri: exportedUri });
} else {
navigation.goBack();
}
}}
/>
);
}When you use VideoEditorSDK directly you do not need VideoEditorHost, and
you should not call openVideoEditor - pick one approach or the other.
Platform Support
- iOS: ✅ Fully supported
- Android: ✅ Fully supported
Example App
A fully working example app is available in the example directory.
cd example
yarn install
cd ios && pod install
yarn iosContributing
- Development workflow:
CONTRIBUTING.md - Code of conduct:
CODE_OF_CONDUCT.md
License
MIT
Built with create-react-native-library
