Files

5.7 KiB

name, description, metadata
name description metadata
rendering Rendering videos with Remotion - CLI, Node.js API, Lambda, and Cloud Run
tags
render, cli, lambda, cloud-run, server, output, mp4, webm, gif

Rendering Videos

CLI Rendering

Render a composition to a video file:

npx remotion render src/index.ts MyComposition out/video.mp4

Common flags

# Set output format
npx remotion render src/index.ts MyComp out.mp4 --codec h264
npx remotion render src/index.ts MyComp out.webm --codec vp8
npx remotion render src/index.ts MyComp out.gif --codec gif

# Set resolution and frame range
npx remotion render src/index.ts MyComp out.mp4 --width 1080 --height 1920
npx remotion render src/index.ts MyComp out.mp4 --frames 0-100

# Increase quality / CRF (lower = better, default 18)
npx remotion render src/index.ts MyComp out.mp4 --crf 15

# Concurrency (parallel frames)
npx remotion render src/index.ts MyComp out.mp4 --concurrency 4

# Pass input props as JSON
npx remotion render src/index.ts MyComp out.mp4 --props '{"title": "Hello"}'

# Or from a file
npx remotion render src/index.ts MyComp out.mp4 --props ./props.json

Render a still image

npx remotion still src/index.ts MyStill out.png
npx remotion still src/index.ts MyStill out.png --frame 30

Available codecs

Codec Extension Use case
h264 .mp4 Default, best compatibility
h265 .mp4 Smaller files, less compatibility
vp8 .webm Web, transparent video
vp9 .webm Better quality WebM
prores .mov Professional editing (Apple ProRes)
gif .gif Short loops, social media

Node.js API Rendering

For server-side rendering, use the @remotion/renderer package:

import { bundle } from "@remotion/bundler";
import { renderMedia, selectComposition } from "@remotion/renderer";
import path from "path";

const render = async () => {
  // Bundle the project
  const bundled = await bundle({
    entryPoint: path.resolve("./src/index.ts"),
  });

  // Select the composition
  const composition = await selectComposition({
    serveUrl: bundled,
    id: "MyComposition",
    inputProps: {
      title: "Hello World",
    },
  });

  // Render
  await renderMedia({
    composition,
    serveUrl: bundled,
    codec: "h264",
    outputLocation: "out/video.mp4",
    inputProps: {
      title: "Hello World",
    },
    onProgress: ({ progress }) => {
      console.log(`Rendering: ${(progress * 100).toFixed(1)}%`);
    },
  });
};

render();

Render a still frame

import { renderStill } from "@remotion/renderer";

await renderStill({
  composition,
  serveUrl: bundled,
  output: "out/thumbnail.png",
  frame: 0,
  inputProps: { title: "Thumbnail" },
});

Render to a buffer (no file)

import { renderMedia } from "@remotion/renderer";

const result = await renderMedia({
  composition,
  serveUrl: bundled,
  codec: "h264",
  outputLocation: null, // No file output
});

// result.buffer contains the video as a Buffer

AWS Lambda Rendering

For serverless rendering at scale. Install the Lambda package:

npx remotion lambda policies role
npx remotion lambda sites create src/index.ts --site-name=my-site
npx remotion lambda functions deploy

Render from code:

import { renderMediaOnLambda } from "@remotion/lambda/client";

const result = await renderMediaOnLambda({
  region: "us-east-1",
  functionName: "remotion-render-...",
  serveUrl: "https://...", // from sites create
  composition: "MyComposition",
  codec: "h264",
  inputProps: {
    title: "Dynamic Video",
  },
});

// result.outputFile - S3 URL of rendered video

Lambda considerations

  • Max 15 min per render (AWS limit)
  • Splits video into chunks, renders in parallel, stitches
  • Cost-effective for burst workloads
  • Use @remotion/lambda for the full API

Google Cloud Run Rendering

Alternative to Lambda using Cloud Run:

npx remotion cloudrun services deploy
npx remotion cloudrun sites create src/index.ts
import { renderMediaOnCloudrun } from "@remotion/cloudrun/client";

const result = await renderMediaOnCloudrun({
  serviceName: "remotion-render",
  region: "us-east-1",
  serveUrl: "https://storage.googleapis.com/...",
  composition: "MyComposition",
  codec: "h264",
  inputProps: { title: "Hello" },
});

Express/HTTP Server Pattern

Expose rendering as an API endpoint:

import express from "express";
import { bundle } from "@remotion/bundler";
import { renderMedia, selectComposition } from "@remotion/renderer";
import path from "path";

const app = express();
app.use(express.json());

// Bundle once at startup
let bundled: string;
bundle({ entryPoint: path.resolve("./src/index.ts") }).then((b) => {
  bundled = b;
  console.log("Bundled and ready");
});

app.post("/render", async (req, res) => {
  const { compositionId, props } = req.body;

  const composition = await selectComposition({
    serveUrl: bundled,
    id: compositionId,
    inputProps: props,
  });

  const result = await renderMedia({
    composition,
    serveUrl: bundled,
    codec: "h264",
    outputLocation: null,
    inputProps: props,
  });

  res.set("Content-Type", "video/mp4");
  res.send(result.buffer);
});

app.listen(3000);

Performance Tips

  • Concurrency: Use --concurrency to render frames in parallel (default: 50% of CPU cores)
  • Bundle once: In server scenarios, call bundle() once and reuse the URL
  • Use calculateMetadata: Pre-compute heavy data before rendering starts
  • Avoid network calls in components: Fetch data via inputProps or calculateMetadata instead
  • Image optimization: Pre-resize images to the exact dimensions needed
  • Memory: For long videos, consider splitting into segments and concatenating