How to Render a Video Locally with Remotion CLI: A Complete Guide
Use npx remotion render [entry] [composition] [output] to bundle your React project, launch headless Chromium, and encode frames into a video file using FFmpeg.
The Remotion CLI provides a deterministic, scriptable interface for transforming React components into video files entirely on your local machine. As implemented in the remotion-dev/remotion repository, the CLI orchestrates bundling, browser management, and FFmpeg encoding through a series of specialized internal modules.
Understanding the Remotion CLI Rendering Pipeline
When you execute npx remotion render, the CLI initiates a seven-stage pipeline defined primarily in packages/cli/src/render-flows/render.ts. This module coordinates between the bundler, headless browser, and rendering engine.
Entry Point and Configuration Parsing
The process begins in packages/cli/src/index.ts, where the cli() function parses process.argv using minimist to identify the render sub-command. Immediately after, getCliOptions() (located in packages/cli/src/get-cli-options.ts) merges your .remotionrc or remotion.config.js with default values, validating parameters like concurrency and codec settings.
Bundling and Composition Resolution
Before rendering can begin, the project must be bundled. The bundleOnCliOrTakeServeUrl() function handles this step, compiling your React code via Webpack. Simultaneously, getCompositionWithDimensionOverride() (from packages/cli/src/get-composition-with-dimension-override.ts) loads the bundled project in a headless browser to extract available compositions. If you haven't specified a composition ID via CLI arguments, the system displays an interactive picker using inquirer.
Browser Launch and Frame Rendering
With the bundle ready, RenderInternals.internalEnsureBrowser() downloads Chromium if necessary, while RenderInternals.internalOpenBrowser() launches the browser instance with your specified flags (such as --chrome-mode="chrome-for-testing"). The actual rendering occurs through two possible paths:
- Image Sequence Mode:
RenderInternals.internalRenderFrames()writes individual frames to disk as JPEG or PNG files. - Video Mode:
RenderInternals.internalRenderMedia()streams rendered frames directly into FFmpeg for encoding and stitching.
Both paths utilize shared progress reporting mechanisms defined in packages/cli/src/progress-bar.ts.
Basic CLI Commands for Local Rendering
The simplest invocation relies on automatic discovery of your entry point and composition:
npx remotion render
This command searches for standard entry files (src/index.tsx, src/index.js, etc.) and prompts you to select a composition if multiple exist. Output defaults to out/<CompositionId>.mp4.
To render a specific composition to a custom location:
npx remotion render src/index.tsx MyComposition ./out/video.mp4
Here, src/index.tsx is the explicit entry point, MyComposition matches the id prop of your <Composition> component, and ./out/video.mp4 specifies the output destination.
Advanced Rendering Options
The CLI exposes dozens of flags to control encoding, performance, and output characteristics. These are processed in packages/cli/src/get-cli-options.ts and passed through to the renderer.
Input Properties and Dynamic Data
Pass dynamic data to your composition using the --props flag:
# Inline JSON
npx remotion render HelloWorld --props='{"title":"Hello world"}'
# External file
npx remotion render HelloWorld --props=./props.json
Inside your component, access these values using getInputProps().
Performance and Quality Controls
Manage system resources and output fidelity:
# Use 4 CPU threads (or 50% of available cores)
npx remotion render HelloWorld --concurrency=4
# Scale frames for higher resolution (1.5×)
npx remotion render HelloWorld --scale=1.5
# Set Constant Rate Factor for quality (requires compatible codec)
npx remotion render HelloWorld --codec=h264 --crf=18
Codec and Format Selection
Control the output encoding:
| Flag | Example | Effect |
|---|---|---|
--codec |
--codec=prores |
Select encoding format (h264, h265, vp8, vp9, prores, gif, etc.). |
--prores-profile |
--prores-profile=4444 |
Specify ProRes quality level (4444, 422, etc.). |
--hardware-acceleration |
--hardware-acceleration=if-possible |
Enable GPU-accelerated encoding when available. |
--pixel-format |
--pixel-format=yuv420p |
Set chroma subsampling for compatibility. |
Complete Advanced Example
Combine multiple flags for a high-quality production render:
npx remotion render src/index.tsx MyComp \
./out/final.mov \
--codec=prores \
--prores-profile=4444 \
--scale=2 \
--concurrency=50% \
--props=./my-props.json \
--hardware-acceleration=if-possible \
--log=verbose
This command bundles the project, renders at 2× scale using half the available CPU cores, encodes to ProRes 4444 with hardware acceleration if possible, and injects properties from my-props.json.
Rendering Image Sequences vs. Video
By default, the CLI renders a single video file. To output individual frames instead, use the --sequence flag:
npx remotion render HelloWorld --sequence --image-format=png
This invokes RenderInternals.internalRenderFrames() instead of RenderInternals.internalRenderMedia(), saving frames to out/HelloWorld/0001.png, 0002.png, etc. The --image-format flag accepts jpeg (default) or png.
Troubleshooting Common Local Rendering Issues
| Issue | Root Cause | Solution |
|---|---|---|
| Chrome cannot decode the video | Default Chromium lacks proprietary codecs. | Use --chrome-mode="chrome-for-testing" which includes full codec support; alternatively install system codecs. |
| Missing props file | --props points to a non-existent path. |
Verify the file path and ensure valid JSON syntax. |
| Out-of-memory during large renders | Simultaneous rendering and encoding exhausts RAM. | Add --disallow-parallel-encoding to separate the rendering and encoding phases. |
| Unexpected output dimensions | Scale or dimension overrides conflict with composition settings. | Ensure --scale ≤ 16 and that --height/--width are multiples of 2 for codec compatibility. |
| File already exists error | Default behavior allows overwrite, but some configurations set --overwrite=false. |
Explicitly add --overwrite to allow replacement of existing files. |
Summary
The Remotion CLI transforms React components into video files through a well-defined local pipeline:
- Entry resolution – automatically discovers
src/index.tsxor uses specified paths viapackages/cli/src/get-cli-options.ts. - Bundling – compiles the project with Webpack using
bundleOnCliOrTakeServeUrl()inpackages/cli/src/render-flows/render.ts. - Browser orchestration – downloads and launches Chromium via
RenderInternals.internalEnsureBrowser()andRenderInternals.internalOpenBrowser(). - Frame generation – renders individual frames with
RenderInternals.internalRenderFrames()or streams to FFmpeg viaRenderInternals.internalRenderMedia(). - Output control – supports multiple codecs, scaling, concurrency limits, and hardware acceleration through flags processed in
packages/cli/src/get-cli-options.ts.
Frequently Asked Questions
What is the minimum command to render a video with Remotion CLI?
The absolute minimum is npx remotion render. This command automatically searches for entry points like src/index.tsx or src/index.js, prompts you to select a composition if multiple exist, and outputs to out/<CompositionId>.mp4. For automated workflows, specify the entry point, composition ID, and output path explicitly: npx remotion render src/index.tsx MyComp out/video.mp4.
How do I pass dynamic data to my Remotion composition when rendering locally?
Use the --props flag to inject JSON data into your composition. You can pass inline JSON: npx remotion render MyComp --props='{"title":"Hello"}', or reference a file: npx remotion render MyComp --props=./data.json. Inside your React component, import getInputProps from remotion to access these values: const {title} = getInputProps().
Why does my rendered video have no audio or fail to play in certain players?
The default Chromium binary used by Remotion lacks proprietary codecs like H.264. According to the source code in packages/cli/src/browser.ts, you should use --chrome-mode="chrome-for-testing" when rendering. This flag ensures the CLI downloads and uses a Chrome-for-Testing binary that includes full codec support, resolving playback issues in standard video players.
How can I render an image sequence instead of a video file?
Add the --sequence flag to output individual frames rather than a stitched video. For example: npx remotion render HelloWorld --sequence --image-format=png. This invokes RenderInternals.internalRenderFrames() instead of the media renderer, saving frames to out/<CompositionId>/0001.png, 0002.png, etc. You can then import these frames into external compositing software like After Effects or DaVinci Resolve.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →