Vibe Coding Academy | Remotion Video Guide

Create Videos with Code Using Remotion + Claude Code

Build professional tutorial videos programmatically with React, AI voiceover, and zero video editing software. The complete 2026 guide.

Overview

What You'll Build

By the end of this guide, you'll have a fully code-driven video production pipeline:

No After Effects. No Premiere Pro. No screen recording. Everything is code — and Claude Code can help you write it.
Before You Start

Prerequisites

Make sure you have the following ready:

Node.js 18+ Required to run Remotion. Check with node --version.
ElevenLabs Account For AI voiceover. Get your API key at elevenlabs.io.
ffmpeg For audio processing and video encoding. Usually pre-installed on Linux.
Claude Code (Optional) For AI-assisted video component development. Not required but makes it 10x faster.
Step 1

Create a Remotion Project

Remotion is a React framework that turns components into video frames. Every frame of your video is a React render — which means you can use all of React's power (props, state, animations) to build videos.

Scaffold a new project:

npx create-video@latest

When prompted, select these options:

Then install dependencies:

cd my-video
npm install

Install Additional Dependencies

We need the ElevenLabs SDK for text-to-speech:

npm install elevenlabs

And make sure ffmpeg is available:

# Ubuntu/Debian
sudo apt install ffmpeg -y

# macOS
brew install ffmpeg

# Verify
ffmpeg -version
Your project structure will look like this: src/ for React components, public/ for audio files, and scripts/ for the voiceover generation script.
Step 2

Understand Remotion Basics

Remotion videos are built from three core concepts:

Key Remotion Hooks

Project Structure

my-video/
├── src/
│   ├── Root.tsx              # Register compositions here
│   ├── components/
│   │   └── TutorialSlide.tsx # Reusable slide component
│   ├── compositions/
│   │   └── MyGuide.tsx       # Video composition
│   └── index.ts
├── public/
│   └── audio/                # Generated voiceover MP3s
├── scripts/
│   └── generate-audio.mjs    # ElevenLabs TTS script
└── package.json
At 30fps, a 2-minute video = 3,600 frames. Each frame is a React render. Remotion handles the rendering pipeline — you just write components.
Step 3

Build a Slide Component

Create a reusable slide component that supports animated titles, staggered bullet points, code blocks, and smooth transitions. This is the core building block of your video.

// src/components/TutorialSlide.tsx
import React from 'react';
import {
  AbsoluteFill, interpolate,
  useCurrentFrame, useVideoConfig, spring,
} from 'remotion';

interface TutorialSlideProps {
  title: string;
  subtitle?: string;
  bulletPoints?: string[];
  codeBlock?: string;
  stepNumber?: number;
  accentColor?: string;
}

export const TutorialSlide: React.FC<TutorialSlideProps> = ({
  title, subtitle, bulletPoints = [],
  codeBlock, stepNumber, accentColor = '#10b981',
}) => {
  const frame = useCurrentFrame();
  const { fps, durationInFrames } = useVideoConfig();

  // Fade in title
  const titleOpacity = interpolate(
    frame, [0, 15], [0, 1],
    { extrapolateRight: 'clamp' }
  );

  // Spring animation for title position
  const titleY = spring({
    frame, fps, from: -30, to: 0, durationInFrames: 20,
  });

  // Fade out at end
  const exitOpacity = interpolate(
    frame,
    [durationInFrames - 15, durationInFrames],
    [1, 0],
    { extrapolateLeft: 'clamp' }
  );

  return (
    <AbsoluteFill style={{
      background: 'linear-gradient(135deg, #0f172a, #1e293b)',
      fontFamily: 'Inter, sans-serif',
      padding: 60,
      opacity: exitOpacity,
    }}>
      <h1 style={{
        fontSize: 44, fontWeight: 800, color: 'white',
        opacity: titleOpacity,
        transform: \`translateY(\${titleY}px)\`,
      }}>{title}</h1>

      {/* Staggered bullet points */}
      {bulletPoints.map((point, i) => {
        const delay = 15 + i * 8;
        const opacity = interpolate(
          frame, [delay, delay + 10], [0, 1],
          { extrapolateRight: 'clamp' }
        );
        return (
          <div key={i} style={{
            opacity, fontSize: 24, color: '#e2e8f0',
            marginTop: 12,
          }}>• {point}</div>
        );
      })}

      {/* Animated code block */}
      {codeBlock && (
        <pre style={{
          background: '#0d1117', borderRadius: 12,
          padding: 24, marginTop: 24,
          fontSize: 20, color: '#e6edf3',
          opacity: interpolate(
            frame, [20, 35], [0, 1],
            { extrapolateRight: 'clamp' }
          ),
        }}>{codeBlock}</pre>
      )}
    </AbsoluteFill>
  );
};
Key concept: interpolate(frame, [start, end], [from, to]) maps the current frame to a value. Use it for opacity, position, scale — anything you want to animate.
Step 4

Write Your Narration Script

Before generating audio, write the voiceover text for each slide. Keep it:

Structure your narration as a JSON array:

const slides = [
  {
    id: 'intro',
    text: "Welcome to Vibe Coding Academy. Today you'll learn
           how to create videos with code using Remotion."
  },
  {
    id: 'step1',
    text: "First, create a new Remotion project by running
           npx create-video at latest in your terminal."
  },
  // ... one entry per slide
];
Pro tip: Read your script out loud before generating audio. If it sounds awkward spoken, rewrite it. AI voices amplify awkward phrasing.
Step 5

Generate AI Voiceover with ElevenLabs

Create a script that calls the ElevenLabs API to generate MP3 files for each slide:

// scripts/generate-audio.mjs
import fs from 'fs';

const VOICE_ID = 'your-voice-id';
const API_KEY  = process.env.ELEVENLABS_API_KEY;

async function generateAudio(text, outputPath) {
  const res = await fetch(
    `https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}`,
    {
      method: 'POST',
      headers: {
        'xi-api-key': API_KEY,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        text,
        model_id: 'eleven_multilingual_v2',
        voice_settings: {
          stability: 0.5,
          similarity_boost: 0.75,
        },
      }),
    }
  );

  const buffer = Buffer.from(await res.arrayBuffer());
  fs.writeFileSync(outputPath, buffer);
  console.log(`✅ ${outputPath}`);
}

// Generate for each slide
for (const slide of slides) {
  await generateAudio(
    slide.text,
    `public/audio/${slide.id}.mp3`
  );
  // Rate limit delay
  await new Promise(r => setTimeout(r, 500));
}

Run it:

ELEVENLABS_API_KEY=your_key node scripts/generate-audio.mjs

Choosing a Voice

ElevenLabs offers dozens of pre-built voices, plus you can clone your own. List available voices:

curl -s "https://api.elevenlabs.io/v1/voices" \
  -H "xi-api-key: YOUR_KEY" | python3 -c "
import sys, json
for v in json.load(sys.stdin)['voices']:
    print(f\"{v['voice_id']}  {v['name']}\")"

Good options for tutorials:

Important: Keep your ElevenLabs API key private. Never commit it to a repository. Use environment variables.
Step 6

Sync Audio Duration to Slides

This is the step most tutorials skip — and it's the most important one. If you hardcode slide durations, your audio will be out of sync with your visuals.

Use ffprobe to measure the exact duration of each audio file:

# Get audio duration in seconds
ffprobe -v error -show_entries format=duration \
  -of csv=p=0 public/audio/intro.mp3

# Output: 14.811429

Convert to frames:

# Formula: frames = ceil(duration × fps) + buffer
# At 30fps: ceil(14.81 × 30) + 15 = 460 frames

# Batch measure all audio files:
for f in public/audio/*.mp3; do
  dur=$(ffprobe -v error -show_entries format=duration \
    -of csv=p=0 "$f")
  frames=$(python3 -c \
    "import math; print(math.ceil(${dur} * 30) + 15)")
  echo "$(basename $f .mp3): ${dur}s → ${frames} frames"
done
Critical: Always measure actual audio duration. Never estimate. A 1-second mismatch compounds across 10 slides into a major sync issue.
Step 7

Create the Video Composition

Wire up your slides with audio using Remotion's Sequence and Audio components:

// src/compositions/MyGuide.tsx
import React from 'react';
import { Audio, Sequence, staticFile } from 'remotion';
import { TutorialSlide } from '../components/TutorialSlide';

// Durations from ffprobe (Step 6)
const slides = [
  {
    id: 'intro',
    durationInFrames: 460,  // Measured!
    slideProps: {
      title: 'My Tutorial',
      subtitle: 'Learn something awesome',
    },
  },
  {
    id: 'step1',
    durationInFrames: 486,  // Measured!
    slideProps: {
      title: 'Step 1: Install',
      codeBlock: 'npm install my-package',
      stepNumber: 1,
    },
  },
  // ... more slides with measured durations
];

export const MyGuide: React.FC = () => {
  let frameOffset = 0;

  return (
    <>
      {slides.map((slide) => {
        const start = frameOffset;
        frameOffset += slide.durationInFrames;
        return (
          <React.Fragment key={slide.id}>
            <Sequence from={start}
              durationInFrames={slide.durationInFrames}>
              <TutorialSlide {...slide.slideProps} />
            </Sequence>
            <Sequence from={start}
              durationInFrames={slide.durationInFrames}>
              <Audio src={staticFile(
                `audio/${slide.id}.mp3`
              )} />
            </Sequence>
          </React.Fragment>
        );
      })}
    </>
  );
};

export const TOTAL_DURATION = slides.reduce(
  (sum, s) => sum + s.durationInFrames, 0
);

Register in Root.tsx

// src/Root.tsx
import { Composition } from 'remotion';
import { MyGuide, TOTAL_DURATION }
  from './compositions/MyGuide';

export const RemotionRoot: React.FC = () => (
  <Composition
    id="MyGuide"
    component={MyGuide}
    durationInFrames={TOTAL_DURATION}
    fps={30}
    width={1920}
    height={1080}
  />
);
Step 8

Preview and Render

Preview in Browser

Remotion includes a studio UI where you can scrub through your video, preview animations, and fine-tune timing:

npm run dev

This opens Remotion Studio in your browser. You can play, pause, and scrub frame-by-frame.

Render to MP4

When you're happy with the preview, render the final video:

npx remotion render MyGuide out/my-guide.mp4

Useful render options:

A 2-minute video takes ~5-10 minutes to render on a standard VPS. Use --concurrency=2 on servers with limited RAM to avoid out-of-memory kills.

Generate Thumbnails

You can also render single frames as images using Remotion's Still component — perfect for YouTube thumbnails:

# Register a Still in Root.tsx, then:
npx remotion still MyThumbnail out/thumbnail.png
Step 9

Using Claude Code for Video Creation

With the Remotion Agent Skills installed (from Step 1), Claude Code understands Remotion's API and can help you build videos faster.

Things Claude Code can do for you:

Example Claude Code Prompt

Create a new Remotion composition that shows a code
editor animation. The code should appear character by
character, like someone is typing it. Use a dark theme
with green text. Add a blinking cursor at the end.

Claude Code will write the full React component using Remotion's API, following the best practices from the installed skills.

Start Claude Code in your project: Open a terminal in your Remotion project folder and run claude. The Agent Skills will automatically load.
Summary

The Full Pipeline

Here's the complete workflow from start to finish:

1
Write narration text One entry per slide, conversational tone, 2-4 sentences each.
2
Generate voiceover Run node scripts/generate-audio.mjs to create MP3 files via ElevenLabs.
3
Measure audio durations Use ffprobe to get exact frame counts for each slide.
4
Build slide compositions Create React components with animations, wire up Sequences + Audio.
5
Preview Run npm run dev to scrub through in Remotion Studio.
6
Render Run npx remotion render to produce the final MP4.
7
Upload Upload to YouTube, social media, or embed on your website.
Tips & Tricks

Making It Your Own

Remotion supports server-side rendering via Remotion Lambda — render videos in the cloud at scale without managing servers.

Want to Learn More?

Join the Vibe Coding Academy community to learn how to build AI-powered apps, automate your workflow, and level up your skills.

Join the Academy
Abdul Khan
Written by
Abdul Khan