How to add a video editor to a Next.js app

Last updated: 30 September 2026

A video editor uses the GPU, workers, WebCodecs and audio in the browser, so in Next.js it has to load on the client only. This guide sets that up with the App Router, handles the two headers the editor’s audio engine wants, and exports video straight from the browser. It uses Cutforge; the same pattern runs in production on a Next.js 16 site.

1. Install

npm install @cutforge/editor

2. A client component for the editor

// app/editor/VideoEditor.tsx
'use client';

import { Editor } from '@cutforge/editor';
import '@cutforge/editor/style.css';

export default function VideoEditor() {
  return (
    <Editor
      license={process.env.NEXT_PUBLIC_CUTFORGE_LICENSE}
      onExportComplete={({ blob, filename }) => upload(blob, filename)}
      style={{ height: '100vh' }}
    />
  );
}

async function upload(blob: Blob, filename: string) {
  const res = await fetch(`/api/upload-url?name=${encodeURIComponent(filename)}`);
  const { url } = await res.json();
  await fetch(url, { method: 'PUT', body: blob, headers: { 'Content-Type': blob.type } });
}

Leave NEXT_PUBLIC_CUTFORGE_LICENSE unset to run the watermarked demo while you build.

3. Load it without server rendering

'use client' alone is not enough: client components still render once on the server, and the editor needs browser APIs from the start. Load it with next/dynamic and ssr: false. In the App Router that option is only allowed inside a client component, so add a small loader:

// app/editor/EditorLoader.tsx
'use client';

import dynamic from 'next/dynamic';

const VideoEditor = dynamic(() => import('./VideoEditor'), {
  ssr: false,
  loading: () => <p>Loading editor…</p>,
});

export default function EditorLoader() {
  return <VideoEditor />;
}
// app/editor/page.tsx
import EditorLoader from './EditorLoader';

export default function Page() {
  return <EditorLoader />;
}

A side benefit: the editor’s code only downloads when someone opens this page.

4. Headers for the fast audio path

The editor’s low-latency audio engine needs cross-origin isolation. Without it the editor still works and falls back to a simpler audio path. Set the headers on the editor route only, so the rest of your site is unaffected:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: '/editor',
        headers: [
          { key: 'Cross-Origin-Opener-Policy', value: 'same-origin' },
          { key: 'Cross-Origin-Embedder-Policy', value: 'require-corp' },
        ],
      },
    ];
  },
};

export default nextConfig;

Three things that catch people out:

5. Save projects

Pass project and onProjectChange to keep the editor’s project in React state, then store it as JSON in your database. The React guide shows the pattern.

Next steps