All reports
Technology by deep-research

Research: Remotion 4.x + Next.js 16 Server-Side Import Chain — Webpack Externals and Dynamic Import Patterns

Narrativ

Research: Remotion 4.x + Next.js 16 Server-Side Import Chain — Webpack Externals and Dynamic Import Patterns

Date: 2026-03-19 Issue: MOKA-309 Unblocks: MOKA-308 (Narrativ production deployment) Author: Deep Research Agent


Executive Summary

The Narrativ build (next build --webpack) passes locally on the VPS (Linux x64). The reported failure occurs on Vercel during production deployment. Root cause analysis reveals three interacting issues:

  1. Vercel may default to Turbopack (Next.js 16 default bundler), ignoring the --webpack flag in package.json scripts
  2. @remotion/compositor-* native binaries are optional dependencies — pnpm may not install them on Vercel if platform differs or optional deps are skipped
  3. The @remotion/renderer and @remotion/bundler packages pull in server-only dependencies that webpack attempts to bundle into client/edge contexts

Current Configuration Analysis

What’s Working (next.config.ts)

serverExternalPackages: [
  "@remotion/compositor-darwin-arm64",
  "@remotion/compositor-darwin-x64",
  "@remotion/compositor-linux-arm64-gnu",
  "@remotion/compositor-linux-arm64-musl",
  "@remotion/compositor-linux-x64-gnu",
  "@remotion/compositor-linux-x64-musl",
  "@remotion/compositor-win32-x64-msvc",
  "@remotion/bundler",
  "@remotion/renderer",
  "@remotion/media-parser",
  "tsconfig-paths-webpack-plugin",
  "enhanced-resolve",
  "esbuild",
],
webpack: (config) => {
  config.externals = [
    ...(Array.isArray(config.externals) ? config.externals : []),
    "esbuild",
    "enhanced-resolve",
  ];
  return config;
},

Verdict: This config is correct for webpack mode. The serverExternalPackages properly excludes all compositor binaries and server-only packages. Local build passes cleanly.

What’s Likely Failing on Vercel

IssueCauseEvidence
Turbopack defaultNext.js 16 uses Turbopack by default; next build in package.json uses --webpack but Vercel may overrideNext.js 16 migration guide states Turbopack is default
Missing compositor binaries@remotion/compositor-linux-x64-gnu is an optional dep; not installed locally (confirmed), may fail to resolve on Vercells node_modules/@remotion/ shows no compositor packages
tsconfig-paths-webpack-plugin bundlingListed in serverExternalPackages but may still be imported transitively by @remotion/bundlerKnown issue in Remotion docs
Client bundle contamination@remotion/renderer is imported somewhere accessible to client webpack passImport chain audit needed

Fix 1: Ensure Webpack Mode on Vercel (CRITICAL)

Create vercel.json to explicitly set the build command:

{
  "buildCommand": "next build --webpack",
  "framework": "nextjs"
}

Why: Vercel auto-detects Next.js and may run next build without --webpack. Since Next.js 16 defaults to Turbopack, and the project has a webpack config in next.config.ts, Turbopack will reject the build entirely.

Fix 2: Separate Client and Server Remotion Usage (HIGH)

The project currently imports @remotion/renderer and @remotion/bundler as top-level dependencies. These are server-only packages that should NEVER appear in any file that could be imported by a client component or page.

Current risk: If any import chain from a page or layout imports these packages, webpack will try to bundle their native dependencies into the client bundle.

Action: Audit all imports of @remotion/renderer and @remotion/bundler:

grep -r "@remotion/renderer\|@remotion/bundler" src/ --include="*.ts" --include="*.tsx"

These should ONLY appear in:

  • src/app/api/ routes (server-only by definition)
  • src/lib/server/ files
  • src/trigger/tasks/ (background jobs)

If found in any other location, wrap with dynamic import:

// BAD: Top-level import in a file that might be reached from client
import { bundle } from "@remotion/bundler";

// GOOD: Dynamic import in API route or server action
const { bundle } = await import("@remotion/bundler");

Fix 3: Add Missing Externals to Webpack Config (HIGH)

Expand the webpack externals to cover all Remotion server packages and their transitive deps:

webpack: (config, { isServer }) => {
  if (isServer) {
    config.externals = [
      ...(Array.isArray(config.externals) ? config.externals : []),
      "esbuild",
      "enhanced-resolve",
      "@remotion/bundler",
      "@remotion/renderer",
      "@remotion/compositor-linux-x64-gnu",
      "@remotion/compositor-linux-x64-musl",
      "@remotion/compositor-linux-arm64-gnu",
      "@remotion/compositor-linux-arm64-musl",
      "@remotion/compositor-darwin-arm64",
      "@remotion/compositor-darwin-x64",
      "@remotion/compositor-win32-x64-msvc",
      "@remotion/media-parser",
      "tsconfig-paths-webpack-plugin",
    ];
  }
  return config;
},

Key change: Use isServer guard so externals only apply to server-side webpack compilation. Client compilation should never encounter these imports at all (if Fix 2 is applied).

Fix 4: Handle Missing Optional Dependencies Gracefully (MEDIUM)

The compositor packages are optionalDependencies of @remotion/renderer. On Vercel (Linux x64), only @remotion/compositor-linux-x64-gnu would be installed — but since they’re optional, pnpm might skip them entirely.

Add to .npmrc or package.json:

# .npmrc
optional=true

Or add the linux compositor as a direct dependency:

{
  "dependencies": {
    "@remotion/compositor-linux-x64-gnu": "4.0.423"
  }
}

Alternatively, if rendering is NOT done on Vercel (it’s done via Trigger.dev workers), then @remotion/renderer should be a devDependency and excluded from the Vercel build entirely.

If video rendering happens via Trigger.dev (background jobs on separate infrastructure), then Vercel only needs the Player (client-side preview). The renderer, bundler, and compositor packages are unnecessary.

Strategy:

  1. Move @remotion/renderer, @remotion/bundler, and @remotion/cli to devDependencies
  2. Keep only @remotion/player, remotion, and composition packages in dependencies
  3. Remove serverExternalPackages for compositor (no longer needed)
  4. API routes that trigger renders should call Trigger.dev, not import renderer directly
{
  "dependencies": {
    "remotion": "4.0.423",
    "@remotion/player": "4.0.423",
    "@remotion/animation-utils": "4.0.423",
    "@remotion/lottie": "4.0.423",
    "@remotion/noise": "4.0.423",
    "@remotion/paths": "4.0.423",
    "@remotion/shapes": "4.0.423",
    "@remotion/three": "4.0.423",
    "@remotion/transitions": "4.0.423",
    "@remotion/motion-blur": "4.0.423"
  },
  "devDependencies": {
    "@remotion/renderer": "4.0.423",
    "@remotion/bundler": "4.0.423",
    "@remotion/cli": "4.0.423"
  }
}

This is the cleanest solution — it eliminates the entire class of server-side import problems.

tsconfig-paths-webpack-plugin Warning

Remotion docs specifically warn that TypeScript aliases can cause import {} from "remotion" to map to the local src/remotion/ folder instead of the npm package. This is a known issue.

Check: Verify tsconfig.json paths don’t shadow the remotion package:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

If src/remotion/ exists (it does in Narrativ), imports like from "remotion" might resolve incorrectly with certain webpack plugins. The current @/* alias is safe, but if tsconfig-paths-webpack-plugin is ever added to the Next.js webpack config, this could break.

Next.js 16 Specific Considerations

FeatureImpact
Turbopack defaultMust use --webpack flag since project has webpack config
serverExternalPackagesReplaces deprecated serverComponentsExternalPackages
React 19Remotion 4.0.423 is compatible with React 19
Middleware → ProxyDeprecation warning shown but not blocking
use clientOnly video-canvas.tsx uses it — correct pattern

Implementation Checklist

  • Create vercel.json with explicit --webpack build command
  • Audit @remotion/renderer and @remotion/bundler import chains
  • Move renderer/bundler/cli to devDependencies if rendering is via Trigger.dev
  • Add isServer guard to webpack externals config
  • Test build on Vercel after changes
  • If render API routes exist on Vercel, add @remotion/compositor-linux-x64-gnu as direct dep

Sources

Related Reports