ffmpegnextjsvideo-processingserverlessapibucket-doc

How to Use FFmpeg with Next.js (No Binary Required)

·Javid Jamae·6 min read
How to Use FFmpeg with Next.js (No Binary Required)

You need to process video in your Next.js app. Maybe you're building a SaaS that converts user uploads, generates thumbnails from product demos, or adds watermarks before hosting. You search "ffmpeg next.js" and the advice all points the same direction: install the binary.

That advice breaks the moment you deploy to Vercel.

Quick answer: Next.js API routes and Server Actions run in serverless environments that can't execute FFmpeg binaries. Use a cloud FFmpeg API like FFmpeg Micro instead. One HTTP call from your route handler, no binary installation, no timeout issues.

Why FFmpeg won't work in Next.js API routes

Next.js Route Handlers (App Router) and API Routes (Pages Router) run as serverless functions on Vercel. Three things make FFmpeg a non-starter:

  • No binary access. Serverless functions run in minimal containers. You can't install system packages or ship native binaries alongside your Next.js bundle.
  • 10-second timeout on the Hobby plan. Even if you could install FFmpeg, most video operations take longer than the default function timeout. Vercel Pro bumps this to 60 seconds, but a 4K transcode can easily take minutes.
  • Cold start overhead. FFmpeg binaries are 80-200MB. Bundling one (even with a Lambda layer hack) blows up your cold start time.

Libraries like fluent-ffmpeg and @ffmpeg/ffmpeg (the WebAssembly port) don't fix this. fluent-ffmpeg still needs the system binary. The WASM port runs in-process but hits memory limits and is 5-10x slower than native FFmpeg.

The cloud API approach

Instead of running FFmpeg inside your Next.js function, send the video to a cloud API that runs FFmpeg for you. The function just makes an HTTP request and returns the result.

FFmpeg Micro is a cloud FFmpeg API built for exactly this. You send it a video URL, tell it what to do, and get the processed file back. Works from any Next.js route handler, Server Action, or middleware. No dependencies to install.

Process video in a Next.js Route Handler

Here's a complete App Router route handler that transcodes a video to MP4 at 720p:

// app/api/transcode/route.ts
import { NextResponse } from 'next/server';

const FFMPEG_MICRO_API_KEY = process.env.FFMPEG_MICRO_API_KEY!;

export async function POST(request: Request) {
  const { videoUrl } = await request.json();

  const response = await fetch('https://api.ffmpeg-micro.com/v1/transcodes', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${FFMPEG_MICRO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      inputs: [{ url: videoUrl }],
      outputFormat: 'mp4',
      preset: {
        quality: 'high',
        resolution: '720p',
      },
    }),
  });

  const job = await response.json();
  return NextResponse.json({ jobId: job.id, status: job.status });
}

That's it. No binary, no WASM, no Docker config. The function fires in milliseconds because all the heavy lifting happens on FFmpeg Micro's infrastructure.

Poll for completion and download

Transcoding is async. You submit the job, then poll until it finishes:

// app/api/transcode/[id]/route.ts
import { NextResponse } from 'next/server';

const FFMPEG_MICRO_API_KEY = process.env.FFMPEG_MICRO_API_KEY!;
const BASE = 'https://api.ffmpeg-micro.com';

export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;

  const statusRes = await fetch(`${BASE}/v1/transcodes/${id}`, {
    headers: { 'Authorization': `Bearer ${FFMPEG_MICRO_API_KEY}` },
  });
  const job = await statusRes.json();

  if (job.status === 'completed') {
    const downloadRes = await fetch(`${BASE}/v1/transcodes/${id}/download`, {
      headers: { 'Authorization': `Bearer ${FFMPEG_MICRO_API_KEY}` },
    });
    const { url } = await downloadRes.json();
    return NextResponse.json({ status: 'completed', downloadUrl: url });
  }

  return NextResponse.json({ status: job.status });
}

The download endpoint returns a signed URL valid for 10 minutes. Your frontend can fetch the processed video directly from that URL without proxying through your Next.js server.

Use it in a Server Action

Server Actions work the same way. Useful if you want to kick off a transcode from a form submission:

// app/actions/transcode.ts
'use server';

export async function transcodeVideo(videoUrl: string) {
  const response = await fetch('https://api.ffmpeg-micro.com/v1/transcodes', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FFMPEG_MICRO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      inputs: [{ url: videoUrl }],
      outputFormat: 'mp4',
      options: [
        { option: '-c:v', argument: 'libx264' },
        { option: '-crf', argument: '23' },
        { option: '-c:a', argument: 'aac' },
      ],
    }),
  });

  const job = await response.json();
  return { jobId: job.id, status: job.status };
}

This example uses options instead of preset for more control over the encoding. You can pass any FFmpeg flag that FFmpeg Micro supports. The preset shorthand is simpler for common operations; options gives you the full FFmpeg command line.

Common pitfalls

Forgetting to set the API key as an environment variable. Add FFMPEG_MICRO_API_KEY to your .env.local for development and to your Vercel project settings for production. Never hardcode it.

Trying to wait for the transcode in one request. A video transcode can take 30 seconds to several minutes. Don't try to hold the HTTP connection open. Submit the job, return the job ID, and poll from the frontend.

Using the wrong input URL format. FFmpeg Micro accepts public HTTP/HTTPS URLs and GCS URLs (gs://). If your video is behind auth, you'll need to either make it temporarily public or upload it through FFmpeg Micro's presigned upload flow first.

Not handling the failed status. Jobs can fail if the input URL is unreachable or the video is corrupted. Always check for status: 'failed' in your polling logic and surface the error to the user.

CLI comparison: what you'd run locally vs. the API

If you had FFmpeg installed locally, you'd run:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac output.mp4

The FFmpeg Micro API equivalent is the POST /v1/transcodes call above. Same operation, same codecs, but the processing runs on managed infrastructure. You're trading "install and maintain FFmpeg" for "make an HTTP call."

FAQ

Can I use ffmpeg.wasm in Next.js instead of a cloud API?

You can, but it runs in-process and is significantly slower than native FFmpeg. It also hits Vercel's memory limits (1024MB on Hobby) on large files. For production workloads, a cloud API is more reliable.

How much does FFmpeg Micro cost for Next.js projects?

FFmpeg Micro has a free tier. You pay per minute of video processed on paid plans. A typical Next.js app processing a few hours of video per month stays well under $20/month.

Does this work with Next.js middleware?

Middleware runs on the Edge Runtime, which has even stricter limits than Node.js serverless functions. You can trigger a transcode from middleware, but it's better suited for route handlers or Server Actions where you have full Node.js APIs.

What about self-hosting Next.js with Docker?

If you self-host on a VPS or Kubernetes, you can install FFmpeg directly and use fluent-ffmpeg or child_process. The cloud API approach still saves you from managing FFmpeg updates, codec licensing, and scaling. But it's a valid path if you want full control.

Last verified: July 2026. Code examples tested against FFmpeg Micro API v1 and Next.js 15 (App Router).

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free