DEV Community

Cover image for Videos in React?! Yep. Each scene is a component (and it costs $0)
Cristina Rodriguez
Cristina Rodriguez

Posted on

Videos in React?! Yep. Each scene is a component (and it costs $0)

I used to think “making a video” meant one of two things:

  1. Open a heavyweight editor
  2. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
  • fps
  • durationInFrames

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}
/>
Enter fullscreen mode Exit fullscreen mode

That’s 10 seconds (300 frames at 30fps).

Want 30 seconds?

Same idea:

  • 30 seconds * 30 fps = 900 frames

So:

durationInFrames={900}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode
  • useCurrentFrame() → tells you which frame is being rendered
  • useVideoConfig() → gives you fps, dimensions, etc.

Now you can animate anything.

Fade in with interpolate()

const contentOpacity = interpolate(frame, [0, 15], [0, 1], {
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
});
Enter fullscreen mode Exit fullscreen mode

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
  },
});
Enter fullscreen mode Exit fullscreen mode

What it does:

  • Creates a natural bounce from 0 → 1
  • frame - 15 delays 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;
Enter fullscreen mode Exit fullscreen mode

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>
))}
Enter fullscreen mode Exit fullscreen mode
  • i === 0 highlights 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
Enter fullscreen mode Exit fullscreen mode

That command turns your React composition into a real MP4 file.


Demo + repo


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 dev and 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)