DEV Community

Rajesh Sharma
Rajesh Sharma

Posted on Originally published at nitrakit.dev AI-assisted

Adding a full video editor to a React Native app (Expo dev builds included)

Disclosure: I build Nitrakit, the video editor SDK used in this post.

Your users want to make video in your app, and "trim and post" isn't enough for them. This guide shows how to add a complete React Native video editor to your app, Expo included, with Nitrakit: install, show the editor, use your own media library and export an MP4. The code
comes straight from the React Native guide in our docs.

Why the editor belongs inside your app

When editing happens somewhere else, users leave. They export, switch apps, edit, and maybe come back to post. Each step is a chance to lose the post, and the moment of making something happens in someone else's product instead of yours.

The other option, building the editor yourself, means a timeline, text, captions, effects, audio and export, on iOS and Android, kept working release after release. For most app teams that is a second product, and it pulls engineers off the one you're actually building.

Nitrakit is the third option: a complete editor you add as a component, styled as your app.

What your users get

The editor your users already know how to use:

  • A preview that plays in real time. Users tap a layer to select it, then drag, pinch or use its handles to move, resize and rotate it.
  • A timeline with tools. Trim, split, speed, volume, effects, transitions, text with animations, audio, and undo.
  • Word-timed captions from your app's own speech-to-text, so you choose the provider.
  • Export to MP4 on the device, while the user keeps editing.

Editing and export run on the user's device, so there's no video server for you to run. Projects save as JSON that you own, so users can come back to a draft later or on another device.

What stays yours: where media comes from and where it's stored, the wording, and the look of the whole editor. More on that below.

The editor's text style presets open, with a highlighted title on the preview

Requirements

From the React Native guide:

  • React Native with the New Architecture (the default since 0.76) and react-native-nitro-modules.
  • iOS 16 or later; Android 8.0 or later (minSdkVersion 26).
  • Expo: a development build. Expo Go can't load the SDK's native code.

If you're on Expo, that last point is the only difference: you run your own development build instead of Expo Go, as you would for any library with native code.

Install the React Native video editor

The SDK is on npm, so there's nothing to set up first. The full steps are on the install page.

npm install @nitrakit/react-native react-native-nitro-modules
Enter fullscreen mode Exit fullscreen mode
  • iOS: run pod install. If users pick from their photo library, add NSPhotoLibraryUsageDescription to your Info.plist.
  • Android: nothing extra to add.

Raise minSdkVersion to 26 if it's lower.

Add it to an Expo app

On Expo, add the config plugin to app.json instead of editing native files. It sets the
Android version for you:

{ "expo": { "plugins": ["@nitrakit/react-native"] } }
Enter fullscreen mode Exit fullscreen mode

Then run npx expo prebuild and build your development build as usual. Your team keeps the Expo workflow it already has.

Create a project

An editor holds one project and its undo history. Create it with your output size and frame rate, then add the user's first video:

import { createEditor, importFile } from '@nitrakit/react-native'

const editor = createEditor(1080, 1920, 30)

const media = editor.addMedia(assetId, importFile(pickedUri, 'clip.mp4'), 'clip.mp4')
const track = editor.addTrack('visual', true)
editor.insertMedia(track, media.source, 0, 0, media.durationUs)
Enter fullscreen mode Exit fullscreen mode

assetId is your app's id for the file, for example its key in your storage. Projects refer
to media only by these ids, which is what lets a project sync through your backend and reopen
on another device. See Projects and media
for signed URLs and the media resolver.

Show the editor

Two components make up the editor. NitrakitPreview plays the project; NitrakitEditor has the timeline, tools and panels. Give both the same editor and they stay in sync:

import { NitrakitEditor, NitrakitPreview } from '@nitrakit/react-native'

export function EditorScreen({ editor }: { editor: Editor }) {
  return (
    <View style={{ flex: 1, backgroundColor: '#000' }}>
      <NitrakitPreview editor={editor} style={{ flex: 1 }} />
      <NitrakitEditor editor={editor} style={{ height: 400 }} onRequest={onRequest} />
    </View>
  )
}
Enter fullscreen mode Exit fullscreen mode

Create the editor once per project (in a useMemo or your state store), not on every render.

Because they're two components, the layout is yours. Show both, only the timeline, or only the preview with controls you build; the layouts page
covers each.

Use your own media library with onRequest

The editor never opens the photo library or a file browser itself. When the user taps
Add media, picks a font or a colour, the editor asks your app through onRequest. So the screen the user sees is yours, in your design, and media can come from the camera roll, your own content library or your backend.

Return the answer, or a promise of it. Return null if the user backed out, or nothing to let the SDK show its built-in picker:

async function onRequest(request: EditorRequest) {
  switch (request.kind) {
    case 'addMedia':
    case 'addOverlay':
    case 'addAudio':
    case 'addSticker': {
      const picked = await pickFromLibrary() // your picker
      if (!picked) return null
      const path = importFile(picked.uri, picked.fileName)
      const media = editor.addMedia(startUpload(path), path, picked.fileName) // your storage's id
      return { source: media.source }
    }
  }
  // anything else: the built-in picker
}
Enter fullscreen mode Exit fullscreen mode

The built-in pickers cover fonts, colours, text, text styles and filters, so you can start with media only and replace the rest when your designers get to it. Every request and its answer is on the Pickers page.

Export an MP4

Export renders the project to an MP4 in the background, while the user keeps editing:

import { filesDir } from '@nitrakit/react-native'

const path = `${filesDir()}/export-${Date.now()}.mp4`
try {
  await editor.exportVideo(path, editor.exportSettings(1080), (f) => setProgress(f))
  // the file at `path` is ready to save, share or upload
} catch (e) {
  // failed, or cancelled with editor.cancelExport()
}
Enter fullscreen mode Exit fullscreen mode

What happens to the file next is up to your app: save it to the photo library, share it, or upload it to your backend. Settings, progress and cancelling are on the Export page.

Make it look like your app

Product and design teams push back on a screen that looks borrowed.
The editor takes your theme as a prop:

<NitrakitEditor
  editor={editor}
  theme={{
    background: '#101418',
    bar: '#0b0e11',
    accent: '#2ee6a6',
    font: 'Brand Sans',
    clips: { video: '#3355ff', audio: '#18a058' },
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Beyond colours and fonts, you can remove tools you don't offer, add your own, swap the icons, and rename every label in your users' languages. It's all set at runtime, so it can follow your app's dark mode, feature flags and language. See Customisation.

Curious what it adds to your app's download? The App size page lists it per platform, including React Native.

Before you ship

Everything above works without a license key while you build and test. A release build needs one, set once at startup; the License key page shows how.

Next steps

Follow the React Native guide in a development build, or try the editor first in the browser demo. If your app is native, there are versions of this post for iOS and Android.

I'm most interested in how this feels to integrate from the React Native side: the Expo setup, the onRequest pattern, anything that fights your navigation or state management. Comments are open.

Originally published on the Nitrakit blog.

Top comments (0)