Video is everywhere. Product demos, social media clips, personalized onboarding videos, dynamic Open Graph images that are actually short animations. Creating these manually in After Effects or Premiere doesn’t scale. Remotion takes a radically different approach: videos are React components. Every frame is rendered by React, styled with CSS, animated with JavaScript, and exported to MP4. If you can build a UI, you can build a video.
A video is a component
In Remotion, a video is a React component that receives the current frame number. You use that frame to calculate positions, opacities, colors, anything that changes over time. The component renders once per frame, and Remotion captures each render into a video file.
import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate } from 'remotion';
export function HelloWorld() {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const opacity = interpolate(frame, [0, fps], [0, 1], {
extrapolateRight: 'clamp',
});
const translateY = interpolate(frame, [0, fps], [30, 0], {
extrapolateRight: 'clamp',
});
return (
<AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
<h1
style={{
fontSize: 80,
color: 'white',
opacity,
transform: `translateY(${translateY}px)`,
}}
>
Hello World
</h1>
</AbsoluteFill>
);
}
useCurrentFrame() returns the frame number starting at 0. interpolate maps a frame range to a value range, the core utility for all animations. At 30fps, frame 0 to 30 means the first second. The text fades in and slides up over that duration. No keyframe editor, no timeline, just math and React.
Composition and timeline
A Remotion project is a collection of compositions. Each composition defines its dimensions, frame rate, duration, and root component.
import { Composition } from 'remotion';
import { HelloWorld } from './HelloWorld';
import { ProductDemo } from './ProductDemo';
export function RemotionRoot() {
return (
<>
<Composition
id="HelloWorld"
component={HelloWorld}
durationInFrames={90}
fps={30}
width={1920}
height={1080}
/>
<Composition
id="ProductDemo"
component={ProductDemo}
durationInFrames={300}
fps={30}
width={1920}
height={1080}
/>
</>
);
}
Compositions are independent videos. The Remotion Studio lets you preview each one in the browser with a timeline, scrub through frames, and render when ready. It’s a video editing environment that happens to be a React dev server.
Sequences: structuring scenes
Real videos have multiple scenes. <Sequence> offsets a component in time, so it appears at a specific frame and has its own internal frame counter starting at 0.
import { AbsoluteFill, Sequence } from 'remotion';
export function ProductDemo() {
return (
<AbsoluteFill style={{ backgroundColor: '#0a0a0a' }}>
<Sequence from={0} durationInFrames={60}>
<TitleCard text="Introducing Acme" />
</Sequence>
<Sequence from={60} durationInFrames={120}>
<FeatureShowcase />
</Sequence>
<Sequence from={180} durationInFrames={90}>
<CallToAction />
</Sequence>
<Sequence from={0} durationInFrames={270}>
<BackgroundMusic />
</Sequence>
</AbsoluteFill>
);
}
Each <Sequence> is a self-contained scene. The TitleCard component’s useCurrentFrame() returns 0 at frame 0 of the video, but FeatureShowcase’s returns 0 at frame 60. Scenes don’t need to know their absolute position in the timeline. They only care about their local time.
The BackgroundMusic sequence spans the entire video. Audio and visual sequences layer naturally, just like elements in a React tree.
Spring animations for natural motion
interpolate produces linear transitions by default. For more natural motion, Remotion provides a physics-based spring function that simulates damping and mass.
import { useCurrentFrame, useVideoConfig, spring, AbsoluteFill } from 'remotion';
export function AnimatedLogo() {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
frame,
fps,
config: { damping: 12, stiffness: 200 },
});
const rotation = spring({
frame: frame - 10,
fps,
config: { damping: 15 },
});
return (
<AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
<div
style={{
transform: `scale(${scale}) rotate(${rotation * 360}deg)`,
}}
>
<Logo />
</div>
</AbsoluteFill>
);
}
The spring starts at 0 and settles at 1, with overshoot and bounce determined by the damping and stiffness values. Passing frame - 10 delays the animation by 10 frames. Springs compose naturally, and because they’re deterministic (no real time, just frame numbers), every render produces the exact same output.
Data-driven videos
The real power of Remotion is generating videos from data. Product catalogs, personalized welcome videos, weekly report summaries, anything that follows a template but varies in content.
import { AbsoluteFill, Sequence, Img } from 'remotion';
interface ReportData {
userName: string;
metrics: { label: string; value: number; change: number }[];
chartUrl: string;
}
export function WeeklyReport({ userName, metrics, chartUrl }: ReportData) {
return (
<AbsoluteFill style={{ backgroundColor: '#0f172a', padding: 80 }}>
<Sequence from={0} durationInFrames={60}>
<FadeIn>
<h1 style={{ color: 'white', fontSize: 48 }}>Weekly Report for {userName}</h1>
</FadeIn>
</Sequence>
<Sequence from={60} durationInFrames={120}>
<MetricsGrid metrics={metrics} />
</Sequence>
<Sequence from={180} durationInFrames={90}>
<Img src={chartUrl} style={{ width: '100%', borderRadius: 16 }} />
</Sequence>
</AbsoluteFill>
);
}
<Composition
id="WeeklyReport"
component={WeeklyReport}
durationInFrames={270}
fps={30}
width={1920}
height={1080}
defaultProps={{
userName: 'Thomas',
metrics: [
{ label: 'Revenue', value: 12400, change: 8.5 },
{ label: 'Users', value: 3200, change: -2.1 },
],
chartUrl: 'https://example.com/chart.png',
}}
/>
Props flow into the video component like any React component. Change the data, get a different video. This is where Remotion becomes a video API: you can programmatically render hundreds of personalized videos by iterating over a dataset and changing the input props.
Audio and transitions
Remotion handles audio the same way it handles visuals. <Audio> and <OffthreadVideo> components are placed in the tree and follow the same frame-based timing.
import { Audio, interpolate, useCurrentFrame } from 'remotion';
import bgMusic from './assets/intro.mp3';
export function Intro() {
const frame = useCurrentFrame();
const volume = interpolate(frame, [0, 30, 150, 180], [0, 0.8, 0.8, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
return (
<>
<Audio src={bgMusic} volume={volume} />
<VisualContent />
</>
);
}
The volume ramps up over the first second, holds steady, and fades out over the last second. Audio mixing is just interpolation on the volume prop. Multiple <Audio> components layer automatically.
For transitions between scenes, you animate both the outgoing and incoming components simultaneously using overlapping sequences.
import { AbsoluteFill, Sequence, interpolate, useCurrentFrame } from 'remotion';
export function CrossFade({ children, durationInFrames }: CrossFadeProps) {
const frame = useCurrentFrame();
const overlap = 15;
return (
<AbsoluteFill>
<Sequence from={0} durationInFrames={durationInFrames}>
<div
style={{
opacity: interpolate(frame, [durationInFrames - overlap, durationInFrames], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
}),
}}
>
{children[0]}
</div>
</Sequence>
<Sequence from={durationInFrames - overlap} durationInFrames={durationInFrames}>
{children[1]}
</Sequence>
</AbsoluteFill>
);
}
No video editing software needed. Transitions are components that control opacity, position, or any CSS property across overlapping time ranges.
Rendering to MP4
The Remotion CLI renders compositions to video files. Under the hood, it opens a headless browser, captures each frame as an image, and encodes them into a video using FFmpeg.
npx remotion render src/index.ts HelloWorld out/hello.mp4
For batch rendering or API-driven workflows, the Node.js API gives full programmatic control.
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
const bundled = await bundle({ entryPoint: './src/index.ts' });
const composition = await selectComposition({
serveUrl: bundled,
id: 'WeeklyReport',
inputProps: { userName: 'Thomas', metrics: [] },
});
await renderMedia({
composition,
serveUrl: bundled,
codec: 'h264',
outputLocation: 'out/report.mp4',
inputProps: { userName: 'Thomas', metrics: [] },
});
This is how you build a video rendering pipeline. A server receives data, calls the render function with custom props, and outputs an MP4. Remotion Lambda takes this further by rendering on AWS Lambda for parallel, scalable video generation.
Conclusion
Remotion turns video production into a software engineering problem. Components replace layers, props replace manual edits, and rendering is a build step. For any use case where videos follow a template, whether that’s social content, product demos, personalized onboarding, or automated reports, writing React is faster and more maintainable than clicking through a timeline. The entire video is code, which means it’s version-controlled, testable, and infinitely reproducible.