Jesse A. Green / notebook

Remotion User Guide

Generated June 2026 from remotion-dev/remotion at 26d3e03

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Remotion lets you create real MP4 videos using React. You write components with JSX, animate them as a function of the current frame, preview everything live in a browser-based Studio, and render the result to a video file — locally, on a server, or in the cloud. If you can build a web page, you can build a video.

This guide is written for the people who use Remotion: motion designers, developers, and teams who want to generate videos programmatically. It covers installation, every core building block, the Studio, rendering, embedding videos in your own app, and the large ecosystem of helper packages. Architecture, deployment internals, and operational concerns live in the companion Admin Guide.

Note

Remotion is React 19-ready and ships as the remotion package (currently 4.0.472) plus a family of @remotion/* packages. Everything in this guide is derived directly from the source code of those packages.


Table of Contents

  1. What Remotion Is
  2. Quick Start
  3. Installation & Requirements
  4. Core Concepts
  5. Defining Compositions
  6. Animating
  7. Sequencing & Timing
  8. Working with Media
  9. Loading Data & Asynchronous Work
  10. The Remotion Studio
  11. Rendering Videos
  12. Embedding Videos with the Player
  13. The Package Ecosystem
  14. Cloud & Server Rendering
  15. Common Workflows
  16. Troubleshooting
  17. FAQ
  18. Licensing
  19. Command & Reference Tables

1. What Remotion Is

Remotion turns React into a video engine. The mental model is simple:

  • A video is a sequence of frames.
  • At any moment, the current frame is just a number.
  • Your React component renders what the video should look like at that frame.
  • To play or render, Remotion advances the frame number and captures each frame.

Because every frame is a pure function of its frame number, your animations are deterministic: the same frame always produces the same pixels, whether you preview it, render it locally, or render it across a thousand cloud workers.

frame 0    frame 15    frame 30    frame 45
  │           │           │           │
  ▼           ▼           ▼           ▼
[render]   [render]    [render]    [render]   →  stitched into video.mp4

You get the full power of the web platform — CSS, SVG, Canvas, WebGL, fonts, the DOM — plus React's component model for composition and reuse.

Tip

If you are new, the fastest way to understand Remotion is to scaffold a project (next section), open the Studio, and scrub the timeline while watching the code update.


2. Quick Start

The fastest path from nothing to a playing video is the project scaffolder.

npm

npx create-video@latest

pnpm

pnpm create video

yarn

yarn create video

bun

bun create video

This launches an interactive wizard that lets you pick a template (Hello World, blank, audiogram, TikTok-style captions, Next.js, Remotion Recorder, and more), then installs dependencies.

Once it finishes:

cd my-video
npx remotion studio
Server ready - Local: http://localhost:3000

The Studio opens in your browser. You'll see a sidebar of compositions, a live preview, and a timeline you can scrub. Edit a file in src/, save, and the preview hot-reloads.

To render the default composition to a file:

npx remotion render
 + Bundled  100%
 + Rendered 100%  out/HelloWorld.mp4

That's the whole loop: scaffold → studio → edit → render.


3. Installation & Requirements

Adding Remotion to an existing project

Remotion is a set of npm packages. The core package is remotion; you almost always also want the CLI:

npm i remotion @remotion/cli

To render programmatically (instead of the CLI), add the renderer and bundler:

npm i @remotion/renderer @remotion/bundler

Important

All remotion and @remotion/* packages must be on the exact same version. Mixing versions causes hard-to-debug errors. When installing, pin exact versions (remove the ^), e.g. npm i remotion --save-exact. Remotion warns at runtime if it detects multiple versions.

Peer dependencies

The remotion package declares React as a peer dependency:

Peer Required version
react >=16.8.0 (React 19 supported and recommended)
react-dom >=16.8.0

System requirements

Requirement Detail
Node.js Node 18+ (or Bun). The Studio and renderer run on Node/Bun.
Chrome/Chromium Used for rendering. Remotion downloads its own Chrome Headless Shell automatically — you don't need a system Chrome.
FFmpeg Bundled with @remotion/renderer. You do not install FFmpeg separately.
OS macOS, Windows, and Linux are supported. On Linux, some shared libraries may be needed for headless Chrome (see the Admin Guide).
Disk Renders write frames to a temp directory before stitching; ensure free space for long videos.

Note

Because Chrome and FFmpeg are managed by Remotion itself, a fresh npm i is usually all you need on a developer machine. Server and Docker setups have additional library requirements covered in the Admin Guide.

Project anatomy

A typical Remotion project looks like this:

my-video/
├── package.json
├── remotion.config.ts      # Studio/render configuration (Config.* calls)
├── public/                 # Static assets — referenced via staticFile()
└── src/
    ├── index.ts            # Calls registerRoot(RemotionRoot)
    ├── Root.tsx            # Lists your <Composition>s
    └── MyComp.tsx          # An actual video component

The entry point (src/index.ts) registers the root component:

import {registerRoot} from 'remotion';
import {RemotionRoot} from './Root';

registerRoot(RemotionRoot);

4. Core Concepts

Before building, internalize these five ideas. Everything else composes from them.

Frames and FPS

A composition has a frame rate (fps) and a length (durationInFrames). A 5-second video at 30 fps is durationInFrames={150}. You convert between seconds and frames yourself: seconds * fps.

useCurrentFrame()

The single most important hook. It returns the frame currently being displayed or rendered, starting at 0.

import {useCurrentFrame} from 'remotion';

const frame = useCurrentFrame(); // 0, 1, 2, ...

Every animation derives from this number.

Important

Do not use setInterval, requestAnimationFrame, CSS animations/transitions, or Date.now() to animate in Remotion. Those are wall-clock based and are not deterministic across frames — they will not render correctly. Drive all motion from useCurrentFrame().

useVideoConfig()

Returns the metadata of the composition you're inside:

import {useVideoConfig} from 'remotion';

const {width, height, fps, durationInFrames} = useVideoConfig();

Compositions

A <Composition> registers a renderable video: an id, dimensions, fps, duration, and the component to render. Compositions appear in the Studio sidebar and are the targets you render from the CLI.

The Root

registerRoot() tells Remotion which component lists your compositions. That component returns one or more <Composition> (and optionally <Folder>) elements.

import {Composition} from 'remotion';
import {MyComp} from './MyComp';

export const RemotionRoot = () => {
  return (
    <Composition
      id="MyComp"
      component={MyComp}
      durationInFrames={150}
      fps={30}
      width={1920}
      height={1080}
    />
  );
};

5. Defining Compositions

<Composition>

Registers a video so it shows in the Studio and can be rendered.

Prop Type Required Description
id string Yes URL-safe unique identifier; the render target and Studio entry name.
component component Yes* The React component to render.
lazyComponent () => Promise<{default}> Yes* Lazy alternative to component (code-splitting).
durationInFrames number Yes** Length of the video in frames.
fps number Yes** Frames per second.
width number Yes** Pixel width.
height number Yes** Pixel height.
defaultProps object No Default props passed to the component (editable in the Studio).
schema Zod object No A Zod schema used to validate and edit props visually in the Studio.
calculateMetadata function No Compute dimensions/fps/duration/props at runtime (e.g. from a video file or API).

* Provide exactly one of component or lazyComponent. ** Required unless a calculateMetadata function supplies them.

<Composition
  id="Promo"
  component={Promo}
  durationInFrames={300}
  fps={30}
  width={1080}
  height={1080}
  defaultProps={{title: 'Hello'}}
/>

Tip

id must be URL-safe — it becomes part of routes and output filenames. Stick to letters, numbers, and dashes.

calculateMetadata — dynamic durations

Often the duration depends on data: the length of an audio file, a fetched dataset, or props. calculateMetadata runs before rendering and can return any of durationInFrames, fps, width, height, props, and default render settings.

import type {CalculateMetadataFunction} from 'remotion';

const calc: CalculateMetadataFunction<{src: string}> = async ({props}) => {
  const durationInSeconds = await fetchAudioDuration(props.src);
  return {
    durationInFrames: Math.floor(durationInSeconds * 30),
    fps: 30,
  };
};

<Composition id="Audiogram" component={Audiogram} schema={mySchema}
  width={1080} height={1080} calculateMetadata={calc} defaultProps={{src: ''}} />

The function receives {defaultProps, props, abortSignal, compositionId, isRendering} and may return its result synchronously or as a Promise. It can also override default render settings: defaultCodec, defaultOutName, defaultVideoImageFormat, defaultPixelFormat, defaultProResProfile, and defaultSampleRate.

<Still>

For single-image outputs (thumbnails, social cards, OG images), use <Still>. It's like <Composition> but has no fps or durationInFrames — a still is one frame.

import {Still} from 'remotion';

<Still id="Thumbnail" component={Thumbnail} width={1200} height={630}
  defaultProps={{title: 'Launch day'}} />

<Folder>

Groups compositions in the Studio sidebar for organization. It has no effect on rendering.

import {Folder} from 'remotion';

<Folder name="Marketing">
  <Composition id="Promo" {/* ... */} />
  <Composition id="Teaser" {/* ... */} />
</Folder>

Type-safe, editable props with Zod

Pass a Zod schema and Remotion renders an editable form for defaultProps in the Studio, validates input props, and infers your component's prop types.

import {z} from 'zod';

const promoSchema = z.object({
  title: z.string(),
  accentColor: z.string(),
});

export const RemotionRoot = () => (
  <Composition
    id="Promo"
    component={Promo}
    schema={promoSchema}
    defaultProps={{title: 'Hello', accentColor: '#0b84f3'}}
    durationInFrames={150} fps={30} width={1920} height={1080}
  />
);

6. Animating

Animation in Remotion is "given the current frame, compute a value." Two functions do most of the work: interpolate and spring.

interpolate()

Maps an input range to an output range — the workhorse for fades, moves, and scales.

import {interpolate, useCurrentFrame} from 'remotion';

const frame = useCurrentFrame();
const opacity = interpolate(frame, [0, 30], [0, 1]);

Signature:

interpolate(input, inputRange, outputRange, options?)
Option Type Default Meaning
easing easing fn or array of fns linear Curve applied between keyframes. As an array, one entry per segment (inputRange.length - 1).
extrapolateLeft 'extend' | 'clamp' | 'identity' | 'wrap' 'extend' Behavior for inputs below the range.
extrapolateRight same 'extend' Behavior for inputs above the range.
posterize number — Snap the input to steps of this size (choppy/stop-motion look).

By default values extend past the range. To hold the start/end values, clamp:

const opacity = interpolate(frame, [0, 30], [0, 1], {
  extrapolateLeft: 'clamp',
  extrapolateRight: 'clamp',
});

Warning

inputRange must be strictly monotonically increasing. interpolate(frame, [0, 30, 30], ...) throws inputRange must be strictly monotonically increasing. inputRange and outputRange must also have the same length.

Multi-segment interpolation

Chain keyframes by passing more than two points (fade in, hold, fade out):

const opacity = interpolate(frame, [0, 20, 130, 150], [0, 1, 1, 0]);

String and tuple output ranges

interpolate can animate CSS strings (scale, translate, rotate) and numeric tuples directly:

// Animate a CSS transform value
const transform = interpolate(frame, [0, 30], ['translateY(0px)', 'translateY(100px)']);
// ⚠ Strings must be the same kind/units across the range.

// Animate a tuple (e.g. an [r, g, b] color)
const rgb = interpolate(frame, [0, 30], [[255, 0, 0], [0, 0, 255]]);

Easing

The Easing module provides curves to pass to interpolate's easing option.

import {interpolate, Easing} from 'remotion';

const x = interpolate(frame, [0, 30], [0, 200], {
  easing: Easing.bezier(0.25, 0.1, 0.25, 1),
  extrapolateRight: 'clamp',
});
Easing Description
Easing.linear No easing.
Easing.ease Default browser-style ease.
Easing.quad, Easing.cubic Power curves.
Easing.poly(n) Generic power curve of degree n.
Easing.sin, Easing.circle, Easing.exp Trigonometric/exponential curves.
Easing.elastic(bounciness) Springy overshoot.
Easing.back(s) Slight backward anticipation.
Easing.bounce Bouncing ball.
Easing.bezier(x1,y1,x2,y2) Cubic Bézier (CSS-style).
Easing.step0, Easing.step1 Step functions.
Easing.in(fn), Easing.out(fn), Easing.inOut(fn) Modifiers that flip/mirror a curve.

spring()

Physics-based animation that feels natural. Always pass frame and fps.

import {spring, useCurrentFrame, useVideoConfig} from 'remotion';

const frame = useCurrentFrame();
const {fps} = useVideoConfig();

const scale = spring({frame, fps, config: {damping: 200}});
Parameter Type Default Meaning
frame number — The current frame (required).
fps number — Frames per second (required).
config.damping number 10 Higher = less oscillation. Must be > 0.
config.mass number 1 Heavier = slower.
config.stiffness number 100 Higher = snappier.
config.overshootClamping boolean false Prevent overshooting past to.
from number 0 Start value.
to number 1 End value.
durationInFrames number — Stretch/squash the spring to a fixed length.
delay number 0 Delay the animation by N frames.
reverse boolean false Play the spring backwards.

Tip

A spring returns ~0 → ~1 by default. Combine it with interpolate to map that progress onto any range, or pass from/to directly. measureSpring() (also exported) tells you a spring's natural duration in frames.

interpolateColors()

Interpolates between colors (hex, rgb, rgba, hsl):

import {interpolateColors, useCurrentFrame} from 'remotion';

const color = interpolateColors(useCurrentFrame(), [0, 30], ['#ff0000', '#0000ff']);

random()

Math.random() is not allowed — it differs every call and breaks deterministic rendering. Use Remotion's seeded random() instead.

import {random} from 'remotion';

const jitter = random('particle-3') * 10; // same seed → same value, every render

Pass a string or number seed and you always get the same pseudo-random value in [0, 1). Passing null falls back to true randomness (rarely what you want).

Caution

Calling random(null) (or Math.random()) inside a component makes renders non-reproducible and can cause flicker between frames during multi-threaded rendering. Always seed.


7. Sequencing & Timing

<Sequence>

Time-shifts its children: they see frame 0 when the sequence starts, and only appear during their window.

import {Sequence} from 'remotion';

<Sequence from={30} durationInFrames={60}>
  <Title />
</Sequence>
Prop Type Default Description
from number 0 Frame at which children start (can be negative).
durationInFrames number Infinity How long children are mounted. Must be positive.
name string — Label shown on the Studio timeline.
layout 'absolute-fill' | 'none' 'absolute-fill' Whether to wrap children in an absolutely positioned div.
premountFor number — Mount (hidden) N frames early so assets/data are ready.
postmountFor number — Keep mounted (hidden) N frames after it ends.
style CSS — Applied to the wrapper (only when layout="absolute-fill").
showInTimeline boolean true Whether to display this sequence on the timeline.

Inside a sequence, useCurrentFrame() returns the local frame (0-based from from):

const Title = () => {
  const frame = useCurrentFrame(); // 0 when the parent <Sequence from={30}> begins
  // ...
};

Note

premountFor is invaluable when a child does data fetching or media loading — it renders the child invisibly ahead of time so it's "warm" when it becomes visible. layout="none" is for when you don't want the wrapping div (e.g. wrapping a <tr> or text).

<Series>

Plays children one after another, auto-computing each one's from from the previous durations. No manual offset math.

import {Series} from 'remotion';

<Series>
  <Series.Sequence durationInFrames={40}><Intro /></Series.Sequence>
  <Series.Sequence durationInFrames={60}><Body /></Series.Sequence>
  <Series.Sequence durationInFrames={40} offset={-10}><Outro /></Series.Sequence>
</Series>

<Series.Sequence> props: durationInFrames (required), offset (shift relative to the previous clip — negative overlaps), plus layout, name, and style.

<Loop>

Repeats its children.

import {Loop} from 'remotion';

<Loop durationInFrames={30} times={4}>
  <Pulse />
</Loop>
Prop Type Default Description
durationInFrames number — Length of one iteration.
times number Infinity How many times to loop (integer ≥ 0).
layout 'absolute-fill' | 'none' 'absolute-fill' Wrapper behavior.

Remotion automatically caps times so the loop never exceeds the composition's duration.

<Freeze>

Freezes its children at a specific frame.

import {Freeze} from 'remotion';

<Freeze frame={60}>
  <Animation />
</Freeze>

It also accepts an active boolean to toggle freezing on and off.


8. Working with Media

Layout: <AbsoluteFill>

A div that fills its parent absolutely (top/left/right/bottom = 0, flex column). The standard way to stack full-screen layers.

import {AbsoluteFill} from 'remotion';

<AbsoluteFill style={{backgroundColor: 'black', justifyContent: 'center'}}>
  <Title />
</AbsoluteFill>

Images: <Img>

Like <img>, but Remotion waits for it to load before capturing the frame (no half-loaded images in your render).

import {Img, staticFile} from 'remotion';

<Img src={staticFile('logo.png')} />

Static files: staticFile()

References a file in your project's public/ folder. Never build asset paths by hand.

staticFile('images/logo.png'); // → /images/logo.png (correctly encoded)

Caution

staticFile() rejects remote URLs, relative paths (./, ..), absolute filesystem paths, and a leading public/. For remote assets, pass the URL directly to src without wrapping it. Since Remotion 4, staticFile() performs URL encoding for you — don't pre-encode.

Audio: <Audio>

Plays audio in preview and includes it in the render.

import {Audio, staticFile} from 'remotion';

<Audio src={staticFile('music.mp3')} volume={0.5} />

Key props: volume (number or a function (frame) => number for fades), trimBefore / trimAfter (trim the clip in frames), playbackRate, muted, toneFrequency, and loopVolumeCurveBehavior.

// Fade the music in over the first 30 frames
<Audio src={staticFile('music.mp3')}
  volume={(f) => interpolate(f, [0, 30], [0, 1], {extrapolateRight: 'clamp'})} />

Video: <OffthreadVideo> and <Video>

For embedding video, <OffthreadVideo> is the recommended component for rendering — it extracts exact frames off the main thread for frame-accurate, reliable output. <Video> (HTML5) is also available.

import {OffthreadVideo, staticFile} from 'remotion';

<OffthreadVideo src={staticFile('clip.mp4')} />
Prop Type Description
src string Required. Local (via staticFile) or remote URL.
trimBefore number Trim N frames from the start. (replaces the deprecated startFrom)
trimAfter number Trim to end at frame N. (replaces the deprecated endAt)
volume number | function Volume, optionally animated per frame.
playbackRate number Speed multiplier.
muted boolean Mute audio.
transparent boolean Render with alpha (e.g. WebM/ProRes pipelines).
toneMapped boolean Apply tone mapping for HDR sources.
toneFrequency number Pitch correction factor.
playbackRate number Speed up / slow down.
audioStreamIndex number Choose which audio track to use.
onVideoFrame function Callback per frame (e.g. to draw to a canvas).
delayRenderTimeoutInMilliseconds / delayRenderRetries number Tune the per-frame fetch timeout/retries.

Important

startFrom/endAt were renamed to trimBefore/trimAfter. The old names still work but are deprecated — prefer the new ones in new code.

Animated images & GIFs

  • <AnimatedImage> (core) plays animated AVIF/WebP/APNG.
  • <Gif> from @remotion/gif plays GIFs frame-accurately (see the ecosystem section).

<IFrame>

Like <iframe>, but participates in Remotion's load-waiting so the render doesn't capture a blank frame.


9. Loading Data & Asynchronous Work

Rendering is frame-by-frame and synchronous by nature, so Remotion needs to know when your async work (data fetch, font load, image decode) is done. That's what delayRender / continueRender do.

delayRender() and continueRender()

Call delayRender() to tell Remotion "don't capture this frame yet," do your async work, then call continueRender(handle).

import {delayRender, continueRender, useCurrentFrame} from 'remotion';
import {useEffect, useState} from 'react';

const Data = () => {
  const [data, setData] = useState(null);
  const [handle] = useState(() => delayRender('Fetching data'));

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then((res) => res.json())
      .then((json) => {
        setData(json);
        continueRender(handle);
      });
  }, [handle]);

  return data ? <Chart data={data} /> : null;
};
delayRender option Type Default Meaning
timeoutInMilliseconds number 30000 How long to wait before failing the frame.
retries number 0 Retry the frame on timeout.

Warning

If a delayRender() handle is never cleared, the render fails with: "A delayRender() … was called but not cleared after 30000ms." Every delayRender() must be matched by a continueRender() (or cancelRender()). Pass a descriptive label so timeouts point you to the culprit.

cancelRender()

Abort the entire render with a clear error (e.g. when a fetch fails irrecoverably):

import {cancelRender} from 'remotion';

fetch(url).catch((err) => cancelRender(err));

prefetch()

Preloads a remote asset (audio/video/image) and returns {free, waitUntilDone}. Useful in the Player to fetch media before playback so it doesn't stutter.

Fonts

Use @remotion/google-fonts for Google Fonts or @remotion/fonts' loadFont() for local fonts — both integrate with the load-waiting system so text is ready before capture (see the ecosystem section).


10. The Remotion Studio

The Studio is a local web app for previewing and configuring your videos. Start it with:

npx remotion studio
# or specify an entry point:
npx remotion studio src/index.ts
Server ready - Local: http://localhost:3000

What you get:

  • Composition list — every <Composition>/<Still>, organized by <Folder>.
  • Live preview — hot-reloads on save.
  • Timeline — scrub frames; sequences, audio, and video show as tracks.
  • Props editor — if a composition has a Zod schema, edit defaultProps with a visual form and validation.
  • Render button — render directly from the UI, with a queue.
  • Visual editing helpers — rulers, guides, keyboard shortcuts.

Tip

The preview command is an alias for studio. Common flags include --port to set the port and --props to pass input props.

Studio behavior is configured in remotion.config.ts via Config.* (covered in the Admin Guide), for example Config.setStudioPort(3001) or Config.setKeyboardShortcutsEnabled(false).


11. Rendering Videos

Render a video

npx remotion render <entry-point?> <composition-id?> <output?>

Examples:

npx remotion render                       # render the only/default composition
npx remotion render MyComp                # render a specific composition
npx remotion render MyComp out/video.mp4  # custom output path
 + Bundled  100%
 + Rendered 150/150
 + out/video.mp4 (2.1 MB)

Render a still image

npx remotion still MyStill out/thumb.png --frame=30

List compositions

npx remotion compositions
The following compositions are available:
  Promo
  Teaser
  Thumbnail

Common render flags

These map directly to the configuration options; pass them on the CLI or set them in remotion.config.ts.

Flag Purpose
--props='{"k":"v"}' or --props=props.json Input props for the composition.
--codec=h264|h265|vp8|vp9|prores|gif Output codec.
--image-format=jpeg|png Frame image format (png for transparency).
--jpeg-quality=80 JPEG frame quality (0–100).
--crf=<n> Quality/Constant Rate Factor (codec-dependent).
--scale=2 Scale output dimensions by a factor.
--frames=0-30 or --frames=30 Render a frame range or single frame.
--concurrency=<n> Parallel rendering tabs (default: half your CPU threads).
--muted Drop the audio track.
--pixel-format=yuva420p Pixel format (e.g. for transparency).
--every-nth-frame=2 For GIFs: pick every Nth frame.
--log=verbose Verbose logging (browser logs, IO).
--gl=angle|swangle|egl|... OpenGL backend for Chrome.
--scale, --width/--height overrides Dimension overrides.

Note

Transparency: render with --codec=vp8/vp9 (WebM) or prores and --image-format=png / --pixel-format=yuva420p. See the Admin Guide's encoding section for the full matrix.

Programmatic rendering (Node SSR)

For build pipelines and servers, render in code with @remotion/bundler + @remotion/renderer:

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

const serveUrl = await bundle({entryPoint: './src/index.ts'});

const composition = await selectComposition({
  serveUrl,
  id: 'MyComp',
  inputProps: {title: 'Hello'},
});

await renderMedia({
  serveUrl,
  composition,
  codec: 'h264',
  outputLocation: 'out/video.mp4',
  inputProps: {title: 'Hello'},
});

Key functions: bundle(), getCompositions() / selectComposition(), renderMedia(), renderStill(), renderFrames(), stitchFramesToVideo(), ensureBrowser(), and makeCancelSignal(). Full option references are in the Admin Guide.


12. Embedding Videos with the Player

@remotion/player lets you embed an interactive Remotion video inside any React app — no rendering, it plays your composition live in the browser.

npm i @remotion/player
import {Player} from '@remotion/player';
import {MyComp} from './MyComp';

export const App = () => (
  <Player
    component={MyComp}
    durationInFrames={150}
    compositionWidth={1920}
    compositionHeight={1080}
    fps={30}
    controls
    style={{width: '100%'}}
    inputProps={{title: 'Hello'}}
  />
);

Selected <Player> props:

Prop Type Description
component / lazyComponent component The composition to play.
durationInFrames, fps, compositionWidth, compositionHeight number Video metadata (required).
inputProps object Props for your component.
controls boolean Show the built-in control bar.
autoPlay boolean Start playing on mount.
loop boolean Loop playback.
clickToPlay, doubleClickToFullscreen, spaceKeyToPlayOrPause boolean Interaction toggles.
allowFullscreen boolean Enable fullscreen.
showVolumeControls boolean Show volume UI.
playbackRate number Speed.
initialFrame number Frame to start at.
inFrame / outFrame number | null Limit playback to a sub-range.
renderLoading, renderPoster functions Custom loading/poster UI.
showPosterWhenPaused / ...WhenEnded / ...WhenBuffering boolean When to show the poster.
renderPlayPauseButton, renderFullscreenButton, renderMuteButton, renderVolumeSlider, renderCustomControls functions Fully custom controls.
numberOfSharedAudioTags number Audio tag pool for autoplay reliability.
schema Zod Validate inputProps.
acknowledgeRemotionLicense boolean Acknowledge the license (suppresses the console notice).

Control the Player imperatively with a ref and the useCurrentPlayerFrame / usePlayer helpers, and listen to events (play, pause, seeked, ended, etc.) via addEventListener on the ref.

<Thumbnail> is a sibling component that renders a single frame as a poster image.

Tip

The Player's component takes your composition component directly — not a <Composition>. Do not mount <Composition> inside a Player; Remotion throws an explicit error if you do.


13. The Package Ecosystem

Remotion ships dozens of @remotion/* packages. Install only what you use; each is versioned in lockstep with remotion. Here are the ones you'll reach for most.

Animation & transitions

@remotion/transitions — animate between scenes with <TransitionSeries>.

import {TransitionSeries, linearTiming, springTiming} from '@remotion/transitions';
import {fade} from '@remotion/transitions/fade';
import {slide} from '@remotion/transitions/slide';

<TransitionSeries>
  <TransitionSeries.Sequence durationInFrames={60}><A /></TransitionSeries.Sequence>
  <TransitionSeries.Transition presentation={fade()} timing={linearTiming({durationInFrames: 30})} />
  <TransitionSeries.Sequence durationInFrames={60}><B /></TransitionSeries.Sequence>
</TransitionSeries>

Built-in presentations include: fade, slide, wipe, flip, clockWipe, iris, none, crossZoom, dreamyZoom, filmBurn, linearBlur, zoomBlur, book-flip, crosswarp, dissolve, ripple, swap, zoom-in-out. Timing functions: linearTiming, springTiming. Hook: useTransitionProgress.

@remotion/animation-utils — helpers like makeTransform and style interpolation for building CSS transform strings cleanly.

Graphics & drawing

Package What it gives you
@remotion/shapes Components <Rect>, <Circle>, <Ellipse>, <Triangle>, <Star>, <Polygon>, <Pie>, <Heart>, <Arrow> + make* functions returning raw path data.
@remotion/paths SVG path math: getLength, getPointAtLength, getTangentAtLength, evolvePath, interpolatePath, warpPath, translatePath, scalePath, reversePath, getBoundingBox, getSubpaths, and more.
@remotion/noise noise2D, noise3D, noise4D simplex noise for organic motion.
@remotion/svg-3d-engine Project and extrude SVG paths in 3D.
@remotion/effects Visual effects like rings and zigzag.
@remotion/motion-blur <CameraMotionBlur> and <Trail> for motion-blur/echo effects.
@remotion/rounded-text-box, @remotion/starburst, @remotion/light-leaks, @remotion/animated-emoji Specialized visual building blocks.

Rendering engines for other tech

Package Use
@remotion/three <ThreeCanvas> — render react-three-fiber / Three.js scenes.
@remotion/skia <SkiaCanvas> — React Native Skia drawing.
@remotion/lottie <Lottie> + getLottieMetadata — play Lottie animations frame-accurately.
@remotion/gif <Gif>, getGifDurationInSeconds, preloadGif — frame-accurate GIFs.
@remotion/rive Render Rive animations.

Audio, captions & media analysis

Package Use
@remotion/media-utils getAudioData, useAudioData, visualizeAudio (waveform/spectrum for audiograms), getVideoMetadata, getImageDimensions, createSmoothSvgPath.
@remotion/captions Caption type, parseSrt, serializeSrt for subtitles.
@remotion/install-whisper-cpp, @remotion/openai-whisper, @remotion/elevenlabs Generate captions / speech via Whisper and ElevenLabs.
@remotion/media-parser parseMedia / downloadAndParseMedia — pure-JS media metadata + frame parsing.
@remotion/webcodecs convertMedia, encoders/decoders — in-browser media conversion via WebCodecs.
@remotion/media New experimental_Audio / experimental_Video components (next-gen media pipeline).

Fonts & assets

Package Use
@remotion/google-fonts getAvailableFonts and per-font loadFont() — load Google Fonts with load-waiting built in.
@remotion/fonts loadFont() for local font files.
@remotion/preload preloadAudio, preloadVideo, preloadImage, preloadFont, resolveRedirect — warm up remote assets for the Player.
@remotion/layout-utils measureText, fitText, fillTextBox — size text precisely (auto-fit headlines).

Tooling

@remotion/tailwind / @remotion/tailwind-v4 (Tailwind support), @remotion/enable-scss (SCSS), @remotion/eslint-plugin and @remotion/eslint-config (linting), @remotion/zod-types (extra Zod types like color pickers in the Studio), @remotion/mcp and @remotion/skills (AI/agent integration).


14. Cloud & Server Rendering

For scale, render in the cloud. These are summarized here for users; full setup is in the Admin Guide.

AWS Lambda — @remotion/lambda

Render thousands of videos in parallel, each across many Lambda invocations.

import {renderMediaOnLambda, getRenderProgress} from '@remotion/lambda/client';

const {renderId, bucketName} = await renderMediaOnLambda({
  region: 'us-east-1',
  functionName: 'remotion-render-...',
  serveUrl: 'https://...your-deployed-site...',
  composition: 'MyComp',
  codec: 'h264',
  inputProps: {title: 'Hello'},
});

const progress = await getRenderProgress({renderId, bucketName, functionName, region});

You first deploy a Lambda function (deployFunction) and a bundled site (deploySite), then render against them. Output lands in S3. The same flags you use locally (codec, crf, scale, frame range, etc.) are accepted as input fields.

Google Cloud Run — @remotion/cloudrun

The GCP equivalent: deploy a Cloud Run service and a site, then render. Note: the Cloud Run integration is offered as an alpha by the Remotion team.

Self-hosted / containers

You can also run the Node SSR APIs (Section 11) on your own server or in Docker. The Admin Guide covers Dockerfiles and the required system libraries.


15. Common Workflows

A typical title animation

import {AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion';

export const Title: React.FC<{text: string}> = ({text}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  const enter = spring({frame, fps, config: {damping: 200}});
  const opacity = interpolate(frame, [0, 20], [0, 1], {extrapolateRight: 'clamp'});

  return (
    <AbsoluteFill style={{justifyContent: 'center', alignItems: 'center'}}>
      <h1 style={{opacity, transform: `scale(${enter})`}}>{text}</h1>
    </AbsoluteFill>
  );
};

An audiogram (waveform synced to audio)

  1. Load the audio with useAudioData from @remotion/media-utils.
  2. Use visualizeAudio to get amplitude bands for the current frame.
  3. Render bars/shapes from those bands.
  4. Add <Audio src={...} /> so the sound is in the output.
  5. Use calculateMetadata to set durationInFrames from the audio length.

The audiogram starter template wires all of this up — scaffold it with npx create-video@latest.

Captioned vertical video (TikTok style)

  1. Generate captions with @remotion/install-whisper-cpp (or import an .srt and parseSrt).
  2. Render words/segments timed with <Sequence> based on caption timestamps.
  3. Use <OffthreadVideo> for the background clip.

The tiktok template demonstrates the full pipeline.

Data-driven videos at scale

  1. Build one parameterized composition with a Zod schema.
  2. Render once per dataset by passing different --props (CLI) or inputProps (Lambda/SSR).
  3. Use calculateMetadata to adapt duration/size per dataset.

16. Troubleshooting

Error: A delayRender() "<label>" was called but not cleared after 30000ms

Cause: An async task started with delayRender() never called continueRender() (the promise rejected, a condition was never met, or the work genuinely takes longer than the timeout). Solution: Ensure every delayRender() is matched by a continueRender(handle) on every code path, including errors. For genuinely slow work, raise the timeout: delayRender('label', {timeoutInMilliseconds: 60000}) or Config.setDelayRenderTimeoutInMilliseconds(60000). Prevention: Always pass a descriptive label; the timeout message includes it and the call stack.

Error: inputRange must be strictly monotonically increasing but got [...]

Cause: An interpolate() inputRange has equal or decreasing values. Solution: Make every value strictly larger than the last (e.g. [0, 30, 60], never [0, 30, 30]). Prevention: When generating keyframes dynamically, sort and de-duplicate them.

Error: inputRange (n) and outputRange (m) must have the same length

Cause: Mismatched array lengths passed to interpolate(). Solution: Provide one output value per input keyframe.

Error: staticFile() does not support remote URLs / relative paths / absolute paths

Cause: Passing a URL, .//.. path, OS path, or a public/-prefixed path to staticFile(). Solution: For remote assets, pass the URL straight to src. For local files, pass the path inside public/ without the public/ prefix, e.g. staticFile('img/logo.png').

Error: Spring damping must be greater than 0

Cause: spring({config: {damping: 0}}). Solution: Use a positive damping value (default is 10).

<Composition> mounted inside another composition / inside the component passed to <Player>

Cause: A <Composition> was placed inside another composition's component tree or passed as the Player's component. Solution: <Composition> belongs only in the Root component. The Player takes your composition component directly, not <Composition>.

Animations work in preview but not in the render

Cause: Using wall-clock animation (CSS transitions/animations, requestAnimationFrame, setInterval, Date.now()) or unseeded Math.random(). Solution: Drive every animated value from useCurrentFrame() and use seeded random().

Video frames look wrong, duplicated, or stuttery in the render

Cause: Using <Video> (HTML5) for rendering, or a non-monotonic source. Solution: Use <OffthreadVideo> for rendering; tune delayRenderTimeoutInMilliseconds/delayRenderRetries for slow remote sources.

Render is very slow

Cause: Low concurrency, heavy per-frame work, or large uncached assets. Solution: Increase --concurrency, use premountFor on data-heavy sequences, prefetch remote media, and consider GPU/hardware acceleration (Admin Guide).

"Multiple versions of Remotion" warning

Cause: remotion / @remotion/* packages on different versions. Solution: Align all of them to the exact same version (--save-exact).


17. FAQ

Q: Do I need to know video editing or FFmpeg? No. You write React. FFmpeg is bundled and used internally; you rarely touch it.

Q: Do I have to install FFmpeg or Chrome? No. @remotion/renderer bundles FFmpeg and downloads its own Chrome Headless Shell automatically.

Q: What video formats can I export? H.264 (default), H.265, VP8, VP9 (WebM), ProRes, GIF, image sequences (PNG/JPEG), and audio-only outputs.

Q: How do I make a transparent video? Render with a codec/pixel format that supports alpha (WebM VP8/VP9 or ProRes) and PNG frames / yuva420p. See the Admin Guide's encoding section.

Q: How do I change the video's duration based on data? Use calculateMetadata on the <Composition> to compute durationInFrames at render time.

Q: How do I pass data into a video? defaultProps on the composition, plus --props on the CLI or inputProps in SSR/Lambda. Validate with a Zod schema.

Q: Can I show a Remotion video in my web app without rendering? Yes — use @remotion/player.

Q: Why is my random animation flickering between frames? You're using Math.random() / random(null). Use a seeded random('some-seed') so each element is stable across frames.

Q: Can I use Tailwind / SCSS / TypeScript? Yes — @remotion/tailwind(/-v4), @remotion/enable-scss, and TypeScript work out of the box.

Q: How do I render lots of videos cheaply and fast? Render on AWS Lambda (@remotion/lambda) or Google Cloud Run (@remotion/cloudrun), parameterizing each render with different input props.

Q: Is Remotion free? Free for individuals, small for-profit companies (up to 3 employees), and non-profits. Larger for-profit organizations need a company license — see below.


18. Licensing

Remotion's source is publicly available, but it is not a standard open-source MIT project — it uses a two-tier license (SEE LICENSE IN LICENSE.md).

Tier Who Cost
Free License Individuals; for-profit organizations with up to 3 employees; non-profit / not-for-profit organizations; anyone evaluating Remotion before commercial use. Free, including commercial video creation.
Company License For-profit organizations above the free-tier size. Paid (via remotion.pro).

Allowed under the free license: using Remotion (commercially or non-commercially) to create videos and images, and modifying the source for your own use or to contribute fixes back.

Not allowed: copying or modifying Remotion to sell, rent, license, or sublicense your own derivative of Remotion.

Important

If your company needs a paid license, obtain a key from remotion.pro and set it with Config.setPublicLicenseKey('...') (or 'free-license' if you qualify for the free tier). The @remotion/licensing package and the Player's acknowledgeRemotionLicense prop relate to this system. This guide summarizes the terms; the authoritative text is in the project's LICENSE.md, and the license changes slightly in Remotion 5.0.


19. Command & Reference Tables

CLI commands

Run any command with npx remotion <command>. Add --help for full flags.

Command Purpose
studio (alias preview) Start the Remotion Studio.
render Render a composition to a video/audio/image-sequence.
still Render a single still image.
compositions List compositions in a project.
bundle Create a deployable bundle of your project.
lambda AWS Lambda subcommands (deploy functions/sites, render, etc.).
cloudrun Google Cloud Run subcommands.
benchmark Benchmark render performance across concurrency settings.
browser Manage the Chrome Headless Shell (e.g. ensure/download).
gpu Report GPU / OpenGL backend info.
ffmpeg / ffprobe Run the bundled FFmpeg / FFprobe.
upgrade Upgrade all Remotion packages together.
versions Show installed Remotion versions and check alignment.
add Add a Remotion package to your project.
skills AI agent skills integration.
help Show CLI help.

Core remotion exports (most-used)

Category Exports
Registration registerRoot, Composition, Still, Folder
Hooks useCurrentFrame, useVideoConfig, useBufferState, useCurrentScale
Animation interpolate, interpolateColors, spring, measureSpring, Easing, random
Layout AbsoluteFill
Timing Sequence, Series, Loop, Freeze
Media Audio, Video, OffthreadVideo, Img, IFrame, AnimatedImage, staticFile
Async delayRender, continueRender, cancelRender, prefetch
Data getInputProps, getStaticFiles, watchStaticFile

Environment variables you may use

Variables prefixed REMOTION_ are exposed to your bundle. Common ones include REMOTION_AWS_ACCESS_KEY_ID / REMOTION_AWS_SECRET_ACCESS_KEY (Lambda credentials), REMOTION_GCP_* (Cloud Run), and a custom .env location via Config.setDotEnvLocation(). The full operational variable list is in the Admin Guide.