I used to think “making a video” meant one of two things:
- Open a heavyweight editor
- Drag tiny clips around a timeline until your soul leaves your body
I’m a developer. I like code. I like components. I like being able to undo mistakes without having to negotiate with a mysterious UI panel from 2002.
So when I heard there’s a tool called Remotion that lets you create videos using React components, my first thought was:
“Sure. And my CSS is also perfectly centered.”
Then I tried it.
And the moment it really clicked wasn’t even the rendering part…
It was running the project like a normal React app.
The idea that makes it click
Remotion works because a video is just:
- A bunch of frames
- Rendered in sequence
- At a fixed fps (frames per second)
If your video is 30fps, then:
- frame
30= 1 second - frame
900= 30 seconds
And here’s the fun part:
Every frame is just React rendering your component (with a different “current frame” number).
So animation becomes… math.
No timeline. No keyframes. Just interpolate() and spring().
“NO COST!!!” (but let’s be precise)
This post is about building and rendering locally on your machine — no SaaS subscriptions, no hosted rendering costs, no paid editor required.
(If you’re using Remotion in a larger company context, double-check licensing. For a personal project / small setup like this, you can build and render locally for free.)
Create the project
Remotion has a starter generator:
npx create-video@latest
It creates a project that looks suspiciously like any other React project.
Like:
picasyfijas-video/
├── public/
│ └── icon-512.png ← Your logo
├── src/
│ ├── Root.tsx ← Registers the composition (dimensions, fps, duration)
│ ├── PicasFijasVideo.tsx ← Main video with all scenes
│ ├── Composition.tsx ← Original template (unused)
│ ├── index.ts ← Entry point
│ └── index.css ← Styles
└── out/
└── picasyfijas.mp4 ← Rendered video
At this point, you’re thinking: “Okay. Cute structure. But where’s the ‘video’ part?”
This is where the brain-melt happens.
The moment it clicked: npm run dev 🤯
I did the most “React developer” thing possible:
npm run dev
And… it just ran like any React project.
The dev server started, I opened the URL, and suddenly I’m staring at a video timeline in my browser.
Scrub the playhead → frames update.
Components appear and disappear.
Animations play.
It’s all live.
And my brain goes:
“Hold up. I’m not editing a video… I’m rendering React.”
That’s the “ohhhhhh” moment.
Because once you realize the preview is just your components rendering over time, everything becomes familiar:
- Want a new “scene”? Make a component.
- Want it to appear later? Wrap it in a
<Sequence from={...} />. - Want animation? Use
useCurrentFrame()+ math.
Same workflow. Same comfort.
But the output is a legit MP4.
Your “video settings” live in one place
Inside src/Root.tsx, you register a Composition.
This defines:
-
width/height fpsdurationInFrames
Here’s the vertical setup I used for a short social video:
<Composition
id="PicasFijasVideo"
component={PicasFijasVideo}
durationInFrames={300}
fps={30}
width={1080}
height={1920}
/>
That’s 10 seconds (300 frames at 30fps).
Want 30 seconds?
Same idea:
30 seconds * 30 fps = 900 frames
So:
durationInFrames={900}
That’s it. Your video is now 30 seconds long.
Scenes = components, and timing = <Sequence />
Your main video component (mine is PicasFijasVideo.tsx) is basically a timeline built with <Sequence />.
Each scene is a component. Each component gets a start frame and duration.
Example:
<Sequence from={0} durationInFrames={90}>
<IntroScene />
</Sequence>
<Sequence from={90} durationInFrames={240}>
<MainScene />
</Sequence>
One very nice detail:
Inside a
<Sequence>,useCurrentFrame()starts at 0 again.
So each scene feels self-contained and easy to reason about.
The 10-second timeline I built (as a reference)
I created a 10-second vertical video (1080×1920, 9:16) for social media using Remotion.
Here’s the timeline:
| Frames | Seconds | Scene | What Happens |
|---|---|---|---|
| 0–30 | 0–1s | Logo | Logo springs in, title fades in |
| 30–90 | 1–3s | GuessScene | Shows "????" and types guess "1456" |
| 90–150 | 3–5s | BullExplanation | Highlights matching "1", shows "🐂 Bull/Fija" |
| 150–210 | 5–7s | CowExplanation | Highlights "4" in wrong position, shows "🐄 Cow/Pica" |
| 210–300 | 7–10s | CTA | Logo + "Play now!" + URL |
For a 30-second video, you simply add more “beats” (more scenes) or stretch durations.
A simple 30-second structure that works for any topic:
- Intro (2–3s)
- 5–7 beats (3–5s each)
- CTA (3–5s)
Animation is just math (and two hooks)
Most scenes start like this:
const frame = useCurrentFrame(); // Current frame number (0, 1, 2... )
const { fps } = useVideoConfig(); // Frames per second (30)
-
useCurrentFrame()→ tells you which frame is being rendered -
useVideoConfig()→ gives youfps, dimensions, etc.
Now you can animate anything.
Fade in with interpolate()
const contentOpacity = interpolate(frame, [0, 15], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
What it does:
- Input: frames
[0..15] - Output: opacity
[0..1] - Result: fade in over 0.5 seconds (at 30fps)
-
clamp: prevents values below 0 or above 1
Bounce with spring()
const highlightScale = spring({
frame: frame - 15, // Start at frame 15
fps,
config: {
damping: 12, // Higher = less bounce
stiffness: 100, // Higher = snappier
},
});
What it does:
- Creates a natural bounce from 0 → 1
-
frame - 15delays the animation start
Deep Dive: BullExplanation scene (a “real” example)
This scene highlights the matching digit and shows the “🐂 Bull / Fija” explanation.
1) Bilingual text toggle
const showSpanish = Math.floor(frame / 15) % 2 === 1 && frame > 45;
What it does:
- Switches language every 15 frames (0.5 seconds)
- Starts toggling after frame 45 (so the content is already visible)
2) Conditional styling for highlights
{secret.split("").map((digit, i) => (
<span
key={i}
style={{
color: i === 0 ? "#EBEBD3" : "#38618C",
backgroundColor: i === 0 ? "#38618C" : "transparent",
transform: i === 0 ? `scale(${highlightScale})` : "scale(1)",
display: "inline-block",
padding: "0 6px",
borderRadius: 8,
}}
>
{digit}
</span>
))}
-
i === 0highlights the first digit (the “bull”) - Highlighted digit gets inverted colors + bounce scale
- Others stay normal
Key Remotion concepts (the cheat sheet)
| Concept | What it does |
|---|---|
Composition |
Defines video dimensions, fps, duration |
Sequence |
Controls when a component appears/disappears on the timeline |
AbsoluteFill |
Full-screen container that fills the canvas |
useCurrentFrame() |
Gets current frame number |
interpolate() |
Maps frame numbers to any value (opacity, position, etc.) |
spring() |
Creates natural bounce animations |
Img + staticFile()
|
Loads images from public/
|
Render to MP4
You can render from the UI… but the CLI is the “ship it” button.
Example:
npx remotion render src/index.ts PicasFijasVideo out/video.mp4
That command turns your React composition into a real MP4 file.
Demo + repo
- Demo video: https://github.com/Yosolita1978/picasyfijas-video/blob/main/out/picasyfijas.mp4
- Repo: https://github.com/Yosolita1978/picasyfijas-video
Wrap-up
Let’s recap:
- Videos are just React renders — one frame at a time.
- Time = frames (30fps → 900 frames = 30 seconds).
-
Scenes = components, and
<Sequence />is your timeline. - Animations are mostly
useCurrentFrame()+ math (interpolate(),spring()). - Rendering is one command:
npx remotion render …→ MP4. - And the wildest part? You can literally start with
npm run devand preview it like… a normal React app. Still feels illegal.
Now I’m curious: what would you make first if “a video” was just a folder of components? A tutorial? A product promo? A meme? A “how to center a div” documentary?
Top comments (0)