RVE LogoReact Video EditorDOCS
RVE Pro/Integration Guides/Vite.js/Advanced Example

Advanced Example

Advanced Next.js integration with React Video Editor

Advanced Next.js Example

A comprehensive Next.js integration with React Video Editor featuring advanced features.

Overview

This example demonstrates advanced features including:

  • Custom video adapters
  • File upload handling
  • Video processing
  • Custom UI components
  • State management
  • API integration

Project Setup

1. Create Next.js Project

npx create-next-app@latest advanced-video-editor --typescript --tailwind --eslint
cd advanced-video-editor

2. Install Dependencies

npm install react-video-editor @types/node
npm install -D @types/react @types/react-dom

3. Create Advanced Video Editor Component

Create components/AdvancedVideoEditor.tsx:

'use client';

import { useState, useCallback } from 'react';
import { ReactVideoEditor, VideoAdapter, ImageAdapter } from 'react-video-editor';

interface VideoProject {
  id: string;
  name: string;
  videoUrl: string;
  thumbnailUrl?: string;
}

export default function AdvancedVideoEditor() {
  const [projects, setProjects] = useState<VideoProject[]>([]);
  const [currentProject, setCurrentProject] = useState<VideoProject | null>(null);
  const [isProcessing, setIsProcessing] = useState(false);

  const handleFileUpload = useCallback(async (file: File) => {
    setIsProcessing(true);
    try {
      // Handle file upload logic here
      const videoUrl = await uploadVideo(file);
      const newProject: VideoProject = {
        id: Date.now().toString(),
        name: file.name,
        videoUrl,
      };
      setProjects(prev => [...prev, newProject]);
      setCurrentProject(newProject);
    } catch (error) {
      console.error('Upload failed:', error);
    } finally {
      setIsProcessing(false);
    }
  }, []);

  const handleExport = useCallback(async () => {
    if (!currentProject) return;
    
    setIsProcessing(true);
    try {
      // Handle export logic here
      await exportVideo(currentProject);
    } catch (error) {
      console.error('Export failed:', error);
    } finally {
      setIsProcessing(false);
    }
  }, [currentProject]);

  return (
    <div className="flex h-screen">
      {/* Sidebar */}
      <div className="w-64 bg-gray-100 p-4">
        <h2 className="text-lg font-semibold mb-4">Projects</h2>
        <div className="space-y-2">
          {projects.map(project => (
            <div
              key={project.id}
              className={`p-2 rounded cursor-pointer ${
                currentProject?.id === project.id ? 'bg-blue-200' : 'bg-white'
              }`}
              onClick={() => setCurrentProject(project)}
            >
              {project.name}
            </div>
          ))}
        </div>
        
        <button
          className="mt-4 w-full bg-blue-500 text-white p-2 rounded"
          onClick={() => document.getElementById('file-input')?.click()}
          disabled={isProcessing}
        >
          {isProcessing ? 'Processing...' : 'Upload Video'}
        </button>
        
        <input
          id="file-input"
          type="file"
          accept="video/*"
          onChange={(e) => {
            const file = e.target.files?.[0];
            if (file) handleFileUpload(file);
          }}
          className="hidden"
        />
      </div>

      {/* Main Editor */}
      <div className="flex-1">
        {currentProject ? (
          <div className="h-full">
            <div className="h-12 bg-gray-200 flex items-center justify-between px-4">
              <h1 className="text-lg font-semibold">{currentProject.name}</h1>
              <button
                className="bg-green-500 text-white px-4 py-2 rounded"
                onClick={handleExport}
                disabled={isProcessing}
              >
                {isProcessing ? 'Exporting...' : 'Export'}
              </button>
            </div>
            <ReactVideoEditor
              width="100%"
              height="calc(100% - 48px)"
              videoUrl={currentProject.videoUrl}
              theme="dark"
              language="en"
              // Advanced configuration
              adapters={{
                video: new VideoAdapter(),
                image: new ImageAdapter(),
              }}
            />
          </div>
        ) : (
          <div className="h-full flex items-center justify-center">
            <p className="text-gray-500">Select a project or upload a video to get started</p>
          </div>
        )}
      </div>
    </div>
  );
}

// Mock functions - replace with actual implementations
async function uploadVideo(file: File): Promise<string> {
  // Implement file upload logic
  return URL.createObjectURL(file);
}

async function exportVideo(project: VideoProject): Promise<void> {
  // Implement video export logic
  console.log('Exporting video:', project.name);
}

4. Create API Routes

Create app/api/upload/route.ts:

import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  try {
    const formData = await request.formData();
    const file = formData.get('file') as File;
    
    if (!file) {
      return NextResponse.json({ error: 'No file provided' }, { status: 400 });
    }

    // Implement file upload logic here
    // This could involve uploading to a cloud storage service
    
    return NextResponse.json({ 
      success: true, 
      url: `/uploads/${file.name}` 
    });
  } catch (error) {
    return NextResponse.json({ error: 'Upload failed' }, { status: 500 });
  }
}

5. Update the Main Page

Update app/page.tsx:

import AdvancedVideoEditor from '@/components/AdvancedVideoEditor';

export default function Home() {
  return (
    <main className="min-h-screen">
      <AdvancedVideoEditor />
    </main>
  );
}

Advanced Features

Custom Adapters

import { CustomVideoAdapter } from './adapters/CustomVideoAdapter';

const customAdapter = new CustomVideoAdapter({
  apiKey: process.env.CUSTOM_API_KEY,
  baseUrl: 'https://api.example.com',
});

State Management

import { useReducer } from 'react';

interface EditorState {
  currentTime: number;
  duration: number;
  isPlaying: boolean;
  volume: number;
}

function editorReducer(state: EditorState, action: any): EditorState {
  switch (action.type) {
    case 'SET_CURRENT_TIME':
      return { ...state, currentTime: action.payload };
    case 'SET_DURATION':
      return { ...state, duration: action.payload };
    case 'SET_PLAYING':
      return { ...state, isPlaying: action.payload };
    case 'SET_VOLUME':
      return { ...state, volume: action.payload };
    default:
      return state;
  }
}

Error Boundaries

import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error }: { error: Error }) {
  return (
    <div className="p-4 bg-red-100 border border-red-400 text-red-700 rounded">
      <h2>Something went wrong:</h2>
      <pre>{error.message}</pre>
    </div>
  );
}

// Wrap your component
<ErrorBoundary FallbackComponent={ErrorFallback}>
  <AdvancedVideoEditor />
</ErrorBoundary>

Performance Optimizations

Dynamic Imports

import dynamic from 'next/dynamic';

const ReactVideoEditor = dynamic(
  () => import('react-video-editor').then(mod => ({ default: mod.ReactVideoEditor })),
  { ssr: false }
);

Memoization

import { memo, useMemo } from 'react';

const VideoThumbnail = memo(({ videoUrl }: { videoUrl: string }) => {
  const thumbnailUrl = useMemo(() => {
    // Generate thumbnail logic
    return `${videoUrl}?thumb=true`;
  }, [videoUrl]);

  return <img src={thumbnailUrl} alt="Video thumbnail" />;
});

File Structure

advanced-video-editor/
├── app/
│   ├── api/
│   │   └── upload/
│   │       └── route.ts
│   └── page.tsx
├── components/
│   ├── AdvancedVideoEditor.tsx
│   └── VideoThumbnail.tsx
├── adapters/
│   └── CustomVideoAdapter.ts
├── hooks/
│   └── useVideoEditor.ts
├── types/
│   └── video.ts
└── utils/
    └── videoProcessing.ts

Next Steps

  • Implement real file upload to cloud storage
  • Add video processing with Web Workers
  • Implement user authentication
  • Add collaborative editing features
  • Check out the Components for more customization options

Troubleshooting

Common Issues

  1. Memory Leaks: Ensure proper cleanup in useEffect
  2. Performance: Use dynamic imports and memoization
  3. File Upload: Handle large files with proper chunking
  4. State Management: Use appropriate state management patterns

Getting Help