Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.clipora.dev/docs/llms.txt

Use this file to discover all available pages before exploring further.

This guide is for React.js applications (Create React App, Vite, custom setups).For Next.js, see the Next.js VideoEditor Integration guide.

Prerequisites

Node.js 16+

Node.js version 16.x or higher

React 18.2.0

React version 18.2.0 (not React 19)

Storage Space

~40MB for WASM modules
VideoEditor uses WebAssembly (WASM) for video processing. These files must be copied to your public/ folder.

Installation

1

Install the SDK

npm install @distralabs/media-editor
2

Install Required Dependencies

npm install framer-motion lucide-react
3

Copy WASM Files

Copy the required WASM modules to your public folder:
# Copy MediaInfo WASM module
cp node_modules/@distralabs/media-editor/dist/MediaInfoModule.wasm public/

# Copy FFmpeg workers
cp node_modules/@distralabs/media-editor/dist/worker.js public/
cp node_modules/@distralabs/media-editor/dist/const.js public/
cp node_modules/@distralabs/media-editor/dist/errors.js public/
cp node_modules/@distralabs/media-editor/dist/decode_worker.js public/
cp node_modules/@distralabs/media-editor/dist/encode_worker.js public/

# Copy FFmpeg core
cp -r node_modules/@distralabs/media-editor/dist/umd public/
4

Verify Files

Your public/ folder should contain:
public/
├── MediaInfoModule.wasm    (2.3 MB)
├── worker.js
├── const.js
├── errors.js
├── decode_worker.js
├── encode_worker.js
└── umd/
    └── ffmpeg-core.wasm    (31 MB)
Add this to your package.json:
package.json
{
  "scripts": {
    "postinstall": "node scripts/copy-wasm.js"
  }
}
Create scripts/copy-wasm.js:
scripts/copy-wasm.js
const fs = require('fs');
const path = require('path');

const files = [
  'MediaInfoModule.wasm',
  'worker.js',
  'const.js',
  'errors.js',
  'decode_worker.js',
  'encode_worker.js',
];

files.forEach(file => {
  const src = path.join(__dirname, '../node_modules/@distralabs/media-editor/dist', file);
  const dest = path.join(__dirname, '../public', file);
  fs.copyFileSync(src, dest);
  console.log(`✓ Copied ${file}`);
});

// Copy umd directory
const umdSrc = path.join(__dirname, '../node_modules/@distralabs/media-editor/dist/umd');
const umdDest = path.join(__dirname, '../public/umd');
fs.cpSync(umdSrc, umdDest, { recursive: true });
console.log('✓ Copied umd/ directory');

VideoEditor Interface

Core Props

interface VideoEditorProps {
  // Required
  licenseKey: string;              // Your SDK license key (JWT)
  onClose: () => void;             // Called when user closes editor

  // Optional
  apiUrl?: string;                 // Override license validation API URL
  defaultVideo?: File;             // Initial video file to load
  onExport?: EditorCallback;       // Called when video is exported
  theme?: Record<string, string>;  // Custom theme colors
  showThemeCreator?: boolean;      // Show theme customization UI
  brands?: BrandDetails[];         // Brand presets
}
interface EditorCallback {
  (result: VideoExportResult): void;
}

interface VideoExportResult {
  videoUrl?: string;     // Object URL or data URL of exported video
  base64?: string;       // Base64 encoded video (if small enough)
  blob?: Blob;           // Video blob for upload
  duration?: number;     // Video duration in seconds
  width?: number;        // Video width
  height?: number;       // Video height
  fps?: number;          // Frames per second
}

Step-by-Step Integration

Step 1: Create Your Component

src/components/VideoStudio.js
import React, { useState, useCallback, useRef } from 'react';
import { VideoEditor } from '@distralabs/media-editor';
import '@distralabs/media-editor/dist/index.css';

function VideoStudio() {
  const fileInputRef = useRef(null);
  const [selectedFile, setSelectedFile] = useState(null);
  const [showEditor, setShowEditor] = useState(false);
  const [exportedVideo, setExportedVideo] = useState(null);
  const [isExporting, setIsExporting] = useState(false);

  // Continue to Step 2...
}

export default VideoStudio;

Step 2: Implement File Selection

const handleFileSelect = (e) => {
  const file = e.target.files?.[0];
  if (file && file.type.startsWith('video/')) {
    setSelectedFile(file);
    setShowEditor(true);
  }
};

Step 3: Implement Export Callback

const handleExport = useCallback((result) => {
  console.log('Video export result:', result);
  setIsExporting(false);

  if (result.videoUrl || result.base64) {
    setExportedVideo(result.videoUrl || result.base64);
    setShowEditor(false);
  }
}, []);

const handleClose = () => {
  setShowEditor(false);
  setSelectedFile(null);
};

const handleDownload = () => {
  if (exportedVideo) {
    const link = document.createElement('a');
    link.href = exportedVideo;
    link.download = `edited-video-${Date.now()}.mp4`;
    link.click();
  }
};

Step 4: Render the Editor

return (
  <div>
    {/* Upload UI */}
    {!showEditor && (
      <input
        ref={fileInputRef}
        type="file"
        accept="video/*"
        onChange={handleFileSelect}
      />
    )}

    {/* Video Editor */}
    {showEditor && selectedFile && (
      <div style={{ position: 'fixed', inset: 0, zIndex: 40 }}>
        <VideoEditor
          licenseKey="YOUR_LICENSE_KEY_HERE"
          apiUrl="https://your-api.com/social"
          defaultVideo={selectedFile}
          onClose={handleClose}
          onExport={handleExport}
        />
      </div>
    )}

    {/* Loading overlay */}
    {isExporting && (
      <div className="loading-overlay">
        <p>Exporting your video...</p>
        <p>This may take 1-2 minutes</p>
      </div>
    )}
  </div>
);

Video Processing

Understanding Export Performance

The VideoEditor uses FFmpeg (WebAssembly) for video processing. This happens entirely in the browser.
Expected Processing Times:
  • 720p 10s video: ~30 seconds
  • 1080p 30s video: ~2 minutes
  • 4K video: May not work (memory constraints)
Video processing is not supported on mobile devices due to memory limitations.

Export Options

const handleExport = useCallback((result) => {
  const link = document.createElement('a');
  link.href = result.videoUrl;
  link.download = 'edited-video.mp4';
  link.click();
}, []);

Theme Customization

const videoTheme = {
  'background.primary': '#0f172a',
  'background.secondary': '#1e293b',
  'text.primary': '#ffffff',
  'accent.primary': '#3b82f6',
  'accent.secondary': '#06b6d4',
  'timeline.background': '#1e293b',
};

<VideoEditor
  theme={videoTheme}
  showThemeCreator={false}
  // ... other props
/>

Complete Example

src/components/VideoStudio.js
import React, { useState, useCallback, useRef } from 'react';
import { VideoEditor } from '@distralabs/media-editor';
import '@distralabs/media-editor/dist/index.css';

const videoTheme = {
  'background.primary': '#0f172a',
  'background.secondary': '#1e293b',
  'text.primary': '#ffffff',
  'accent.primary': '#3b82f6',
};

function VideoStudio() {
  const fileInputRef = useRef(null);
  const [selectedFile, setSelectedFile] = useState(null);
  const [showEditor, setShowEditor] = useState(false);
  const [exportedVideo, setExportedVideo] = useState(null);
  const [isExporting, setIsExporting] = useState(false);

  const handleFileSelect = (e) => {
    const file = e.target.files?.[0];
    if (file && file.type.startsWith('video/')) {
      setSelectedFile(file);
      setShowEditor(true);
    }
  };

  const handleExport = useCallback((result) => {
    setIsExporting(false);
    if (result.videoUrl) {
      setExportedVideo(result.videoUrl);
      setShowEditor(false);
    }
  }, []);

  const handleClose = () => {
    setShowEditor(false);
    setSelectedFile(null);
  };

  const handleDownload = () => {
    if (exportedVideo) {
      const link = document.createElement('a');
      link.href = exportedVideo;
      link.download = `edited-video-${Date.now()}.mp4`;
      link.click();
    }
  };

  return (
    <div className="video-studio">
      {!showEditor && (
        <input
          ref={fileInputRef}
          type="file"
          accept="video/*"
          onChange={handleFileSelect}
        />
      )}

      {showEditor && selectedFile && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 40 }}>
          <VideoEditor
            licenseKey="YOUR_LICENSE_KEY"
            apiUrl="https://localhost:3030/social"
            defaultVideo={selectedFile}
            onClose={handleClose}
            onExport={handleExport}
            theme={videoTheme}
            showThemeCreator={false}
          />
        </div>
      )}

      {isExporting && (
        <div className="loading-overlay">
          <p>Exporting your video...</p>
          <p>This may take a moment</p>
        </div>
      )}

      {exportedVideo && (
        <div className="preview">
          <video src={exportedVideo} controls />
          <button onClick={handleDownload}>Download</button>
        </div>
      )}
    </div>
  );
}

export default VideoStudio;

Troubleshooting

Symptom: “MediaInfoModule.wasm 404 Not Found”Solution:
  1. Verify WASM files are in public/ folder
  2. Restart dev server
  3. Hard refresh browser (Cmd+Shift+R)
Symptom: Export hangs or takes >5 minutesSolutions:
  • Use shorter video clips (< 30 seconds recommended)
  • Lower video resolution before editing
  • Consider server-side processing for production
Symptom: VideoEditor fails on mobile devicesSolution: Mobile is not supported. Detect and show warning:
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

if (isMobile) {
  alert('Video editing requires a desktop browser');
}

Next Steps

Image Editor

Learn how to integrate the ImageEditor component

FAQ

Common issues and troubleshooting
Need help? Contact us at support@distralabs.com