Remotion Code Analysis — Bundle System and Webpack/esbuild Integration
Tracing how bundle() transforms React components into renderable HTML through source code
GitHub: remotion-dev/remotion
Remotion rendering starts with bundling. We trace through source code how React components become a browser-executable HTML+JS bundle.
Repository Structure
packages/
├── bundler/ # Webpack-based bundler
│ ├── src/
│ │ ├── bundle.ts # bundle() entry point
│ │ ├── webpack-config.ts # Webpack config generation
│ │ └── index-html.ts # HTML template generation
├── core/ # Core runtime
│ ├── src/
│ │ ├── Composition.tsx # <Composition> component
│ │ ├── CompositionManager.tsx # Manages registered Compositions
│ │ └── register-root.ts # registerRoot() entry
├── renderer/ # Local renderer
└── lambda/ # AWS Lambda rendering
The key is the Webpack config generated by webpackConfiguration(). It uses esbuild-loader for TypeScript transpilation — a hybrid of Webpack bundling + esbuild speed.
The generated index.html is loaded by Headless Chrome. When main.bundle.js executes, React mounts on #remotion-root, and all <Composition> components register with CompositionManager. The renderer then calls window.getCompositions() to determine what to render.
How It Works
When bundle(entryPoint) is called, webpackConfiguration() generates Webpack config
esbuild-loader transpiles TSX → JS, Webpack bundles the dependency graph
index-html.ts generates HTML containing #remotion-root + main.bundle.js
When registerRoot() executes, CompositionManager collects all <Composition> metadata
Renderer queries fps/width/height/durationInFrames via window.getCompositions()
Pros
- ✓ Webpack + esbuild hybrid: both compatibility (Webpack) and speed (esbuild)
- ✓ window.getCompositions() pattern: clean metadata bridge between browser ↔ Node.js
Cons
- ✗ Webpack dependency: initial bundling can be slow (Vite migration under discussion)