Claude Academy
Sign in

Remotion Fundamentals

Remotion's core idea: a video is a React component evaluated once per frame, for frames 0 .. durationInFrames - 1 at a given fps. There is no timeline editor — the timeline is your code, and every visual property is a pure function of the current frame number.

import { useCurrentFrame, useVideoConfig, interpolate } from "remotion";

export const Title: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps, durationInFrames, width, height } = useVideoConfig();
  const opacity = interpolate(frame, [0, 30], [0, 1], {
    extrapolateRight: "clamp",
  });
  return <h1 style={{ opacity }}>Hello at frame {frame}</h1>;
};

The two hooks

  • useCurrentFrame() — the frame being rendered right now; the single source of truth for all animation.
  • useVideoConfig(){ fps, durationInFrames, width, height } of the enclosing composition; use it instead of hard-coding so components survive resizes and retimes.

Animation primitives

interpolate(frame, inputRange, outputRange, options) maps frame ranges to value ranges — [0, 30] → [0, 1] fades in over 30 frames. Critical option: without extrapolateRight: 'clamp' the value keeps going past the range (opacity 1.5, x-position off-screen). Clamping is the default mistake generator for newcomers; reach for {extrapolateLeft/Right: 'clamp'} reflexively.

spring() produces physically-based 0→1 motion:

const scale = spring({ frame, fps,
  config: { damping: 12, stiffness: 100, mass: 1 } });
  • damping — how fast oscillation dies (higher = less bounce)
  • stiffness — spring strength (higher = snappier)
  • mass — inertia (higher = slower, heavier feel)

Composition primitives

<Sequence from={60} durationInFrames={40}> time-shifts its children: inside, useCurrentFrame() restarts at 0 when the parent hits frame 60. This local-frame reset is what makes clips composable — a component animates from "its" frame 0 regardless of where it's placed. Sequences are how you build scenes.

<AbsoluteFill> — a position: absolute; inset: 0 container for stacking layers (background / content / overlay), the video equivalent of z-layers.

<Composition> registers a renderable video with its metadata, wired up via registerRoot():

<Composition id="Explainer" component={Explainer}
  durationInFrames={300} fps={30} width={1920} height={1080}
  defaultProps={{ title: "Prompt Caching" }} />

id is what the studio and render CLI target; defaultProps are overridable at render time with input props (see remotion-player for how the same props flow through interactive playback).

Rendering to a file

Server-side (Node) pipeline:

const bundleLoc = await bundle({ entryPoint });          // webpack bundle
const comp = await selectComposition({ serveUrl: bundleLoc, id: "Explainer" });
await renderMedia({ composition: comp, serveUrl: bundleLoc,
  codec: "h264", outputLocation: "out.mp4" });

renderMedia drives Chrome Headless Shell to screenshot every frame and encode. At scale, @remotion/lambda's renderMediaOnLambda() fans the frame range out across many AWS Lambda invocations and stitches the result — the standard approach for per-user video generation.

Licensing

Remotion is free for individuals and companies of up to 3 people; larger companies need a paid company license. Budget this before adopting it in a commercial product.

Key terms

  • Composition — a registered video: component + id + durationInFrames + fps + width/height + defaultProps, targeted by the studio and render APIs.
  • useCurrentFrame() — hook returning the frame currently being evaluated; all animation derives from it.
  • useVideoConfig() — hook returning {fps, durationInFrames, width, height} of the enclosing composition.
  • interpolate() — maps a frame range to a value range; without extrapolate clamping, values overshoot past the input range.
  • spring() — physics-based 0→1 animation parameterized by damping, stiffness, and mass.
  • Sequence — time-shifting container (from, durationInFrames) inside which the local frame counter restarts at 0.
  • AbsoluteFill — absolutely-positioned full-size container used for layering.
  • registerRoot() — entry-point call that registers your composition list with Remotion tooling.
  • renderMedia() — Node API that renders a composition to a video file (e.g. codec 'h264') using Chrome Headless Shell.
  • renderMediaOnLambda() — @remotion/lambda API that distributes a render across AWS Lambda for scale.
  • Company license — required for companies above 3 people; Remotion is source-available, not fully free for commercial teams.

See also