remotion
remotion — video in React, rendered frame by frame
Remotion renders your React component once per frame (across parallel workers), then stitches the frames into video. Two React habits invert here: time is a prop (the frame), not a side effect, and render must be deterministic. (Distilled in our own way from the Remotion team's remotion-best-practices, MIT. For general React/hooks/state, use react-best-practices.)
The rules
- One clock only. Drive every animation off
useCurrentFrame(). Forbidden (they don't render): CSS transitions/animations, Tailwind animation classes,Date.now(),setTimeout/setInterval,requestAnimationFrame. - Determinism. Output must be a pure function of
(frame, props, useVideoConfig()). No unseeded randomness — userandom(seed)fromremotion, neverMath.random(). Don't read wall-clock or external mutable state. interpolate()by default;spring()only for explicit physics/bounce. Clamp when a value shouldn't run past its range:interpolate(frame, [in, out], [from, to], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }). Custom curves viaeasing: Easing.bezier(...)—Easing.outfor enters,Easing.infor exits.- Read config, don't hardcode. Pull
fps/durationInFrames/width/heightfromuseVideoConfig(). Express durations as seconds × fps (2 * fps= 2s), never a bare frame count that assumes an fps. - Keep animation Studio-editable. Put the
interpolate()call inline in thestyleprop, and animate individual transform props (scale,translate,rotate) — not a composedtransform: `scale(${x})`string. Inline + individual props = Remotion Studio can edit them. - Time with components, not math.
<Sequence from={f} durationInFrames={n}>to delay/limit; child frames are relative to the sequence start.layout="none"for inline (default isAbsoluteFill).<Series>for back-to-back scenes. - Assets. Files go in
public/, referenced viastaticFile("name.ext")(remote URLs also fine).<Img>fromremotion;<Video>/<Audio>from@remotion/media(the current package — older projects use core<Audio>/<OffthreadVideo>; watch the import path). - Async data →
calculateMetadata. For anything you mustfetchbefore rendering, usecalculateMetadata({ props, abortSignal })on<Composition>to set duration/dimensions/props up front; passabortSignalso stale Studio requests cancel;Promise.allfor multiple sources. (delayRender()/continueRender()is the lower-level fallback for async work inside a component.) - Workflow. Scaffold only in an empty folder (
npx create-video@latest). Preview withnpx remotion studio. Cheap sanity check:npx remotion still <id> --frame=N --scale=0.25(frame is zero-based). Render vianpx remotion renderor@remotion/renderer.
Effects & advanced
Reach in this order: plain HTML/CSS/SVG (filter/blend/mask) → a built-in effect from @remotion/* → a custom createEffect() → <HtmlInCanvas onPaint> as a last resort. Remotion has first-class support for captions/subtitles, audio visualization & silence detection, transitions, 3D (Three.js / R3F), Google/local fonts, GIFs, Lottie, MapLibre, trimming, and transparent video — look up the official docs when you hit one. For TTS voiceover or transcription, bring your own key (e.g. ElevenLabs / whisper) — never commit it.