Build Animated Video Overlays

Create a spring-animated lower-third overlay with a design system in Remotion.

Introduction

30 Second Summary

You know those sleek name bars that slide in at the bottom of the screen during interviews, live streams, and conference talks? Those are called lower-thirds, and professionals typically build them in expensive motion graphics software.

In this project, you will build an animated lower-third overlay using Remotion, a framework that lets you create videos entirely with code. You'll design a spring-animated name bar powered by a custom design system, then render it as an MP4.

What You'll Build

A professional animated lower-third overlay driven by a centralized design system, built and rendered inside Remotion.

By the end of this project, you'll have:

  • A Remotion project scaffolded with TailwindCSS and optional Claude Code integration.
  • A spring-animated lower-third component that slides in, displays a name and title, then fades out.
  • A design system file defining your brand's color palette, typography, spacing, and animation config.
  • Secret Mission: Composite the overlay on top of real video footage using Remotion's layer system.

Are there any prerequisites?

No prior experience is needed. This guide includes full setup instructions for all required tools, including Node.js and Remotion.

Set Up Remotion

Every Remotion project runs on Node.js and uses React components to define animations.

In this step, you'll install Node.js, scaffold a new Remotion project with TailwindCSS support, and start up Remotion Studio. By the end, you'll have a live preview environment ready for building animations.

In this step, get ready to:
  • Install Node.js (v16 or higher).
  • Scaffold a new Remotion project with TailwindCSS.
  • Start Remotion Studio to preview your animations.
Install Node.js

Node.js is the JavaScript runtime that powers Remotion under the hood. You need version 16 or higher installed on your machine.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
  • Check if Node.js is already installed by running:
node --version

✔️ I see a version number

You're all set. Make sure the number shown is v16 or higher (e.g. v20.11.0).

Skip ahead to the next substep.

ⓧ It's not installed yet

  • Go to the Node.js download page at https://nodejs.org/en/download.
  • Download the installer for your operating system (the LTS version is recommended).
  • Run the installer and accept the default settings.
  • Close and reopen your terminal after installation.
  • Run node --version again to confirm it shows v16 or higher.

Still seeing 'command not found' after installing?

Make sure you fully closed and reopened your terminal after the installer finished. On Windows, try opening a new Command Prompt window as Administrator.

help me troubleshoot Node.js not being recognized after installation

Scaffold the Remotion project

Remotion provides a scaffold command that sets up a complete project structure for you. It will create a folder with all the configuration files and dependencies your video project needs.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your Remotion project by running this command:
npx create-video@latest

What will this command ask me?

This command launches an interactive wizard that asks you a series of questions. Answer each prompt as described below.

  • When prompted for a project name, type overlay-project and press Enter.
  • When prompted for a template, select Blank and press Enter.
  • When asked about TailwindCSS, select Yes.
  • When asked about installing Skills, select Yes.

What are Skills?

Skills are best-practice instructions installed to a .claude/skills directory. They help AI coding agents (like Claude Code) understand Remotion's patterns. Even if you're not using Claude Code, they're harmless to include.

  • Once the scaffold finishes, move into the project folder and install dependencies by running these commands:
cd overlay-project
npm install
  • Confirm the project was created by running:
ls src

You should see files including Root.tsx and index.ts in the output.

Seeing a network error or timeout?

The scaffold command downloads packages from the internet. Make sure you have a stable internet connection and try running npx create-video@latest again.

If npm install fails, try deleting the node_modules folder and running npm install again.

help me fix npm install errors in my Remotion project

Start Remotion Studio

Remotion Studio is the live preview environment where you'll see your animations play back in real time. It runs in your browser and updates instantly as you edit code.

  • Start Remotion Studio by running:
npm run dev
  • Open the URL shown in your terminal output (typically http://localhost:3000) in your browser.

You should see Remotion Studio load with an empty composition list in the sidebar. This is where your animations will appear once you create them.

Don't see anything in the browser?

Check your terminal output for the exact URL. The port might be different if something else is already using port 3000.

Make sure you're still in the overlay-project folder when you run npm run dev.

help me get Remotion Studio running in my browser

Keep this terminal running

Remotion Studio needs this terminal process to stay active. For future commands, open a second terminal tab or window in the same overlay-project folder.

Your development environment is ready. Next up, you'll create your first animated component and see it play back live in Remotion Studio.

Create Your First Animation

Your Remotion environment is running and ready to go.

Now it's time to get something moving on screen. You'll build an animated lower-third overlay that slides in with spring physics, displays a name and title, then fades out.

In this step, get ready to:
  • Create a theme file with color, typography, and animation tokens.
  • Build the LowerThird animation component.
  • Register and preview the composition in Remotion Studio.
Build the animation component

A lower-third is the name and title bar you see at the bottom of interview videos. You'll build one using Remotion's animation primitives: spring() for physics-based motion and interpolate() for mapping values to visual properties.

There are two ways to build this. Pick the approach that suits you.

Claude Code

Claude Code is an AI coding agent that runs in your terminal. It can generate entire files from a natural language prompt.

What is Claude Code?

Claude Code is an Anthropic product that requires a paid subscription (Max plan at ~$20/month). If you don't have a subscription, use the Manual tab instead.

  • Open a new terminal window in your overlay-project directory.
  • Start Claude Code by running:
claude
  • Once Claude Code is active, paste this prompt:
Create a theme.ts file in src/ that exports a theme object with: colors (primary: #6366F1, secondary: #EC4899, accent: #14B8A6, dark: #0F172A, light: #F8FAFC, overlay: rgba(15,23,42,0.85)), typography (fontFamily using Inter/system-ui/sans-serif, sizes for heading at 48px, subheading at 24px, body at 16px), spacing (sm: 8, md: 16, lg: 24, xl: 32), and animation (springConfig with damping: 12, stiffness: 200, mass: 0.5).

Then create a LowerThird component in src/LowerThird.tsx that animates a name and title sliding in from the left with spring physics using the theme's springConfig. Use a dark semi-transparent background bar with a colored accent stripe on the left edge. Position it at bottom: 80, left: 60. The name should be large white text (heading size) and the title should be smaller in the accent color (subheading size). Add a fade-out interpolation from frame 120 to 140. Accept optional props for name (default 'Alex Johnson'), title (default 'Creative Director'), and accentColor (default theme.colors.primary). Import all style values from theme.ts.

Finally, update src/Root.tsx to import LowerThird and register it as a Composition with id 'LowerThird', 1920x1080, 30fps, 150 frames, with defaultProps for name and title.

What will Claude Code create?

Claude Code will generate three files for you:

  • src/theme.ts with your design tokens (colors, fonts, spacing, animation config).
  • src/LowerThird.tsx with the animated overlay component.
  • src/Root.tsx updated to register the composition.
  • Allow Claude Code to create and edit the files when it asks for permission.
  • Once it finishes, confirm the files exist by running:
ls src/theme.ts src/LowerThird.tsx src/Root.tsx

You should see all three files listed without any errors.

Claude Code not generating the files?

Make sure you started Claude Code from inside the overlay-project directory. If Claude Code asks clarifying questions, confirm and let it proceed.

help me debug Claude Code not generating my Remotion files

Manual

You'll create three files by hand. Start with the theme file, then the animation component, then the composition registration.

  • Create a new file called theme.ts inside your src/ folder by running:
touch src/theme.ts
  • Open src/theme.ts and paste the following code:
export const theme = {
  colors: {
    primary: '#6366F1',
    secondary: '#EC4899',
    accent: '#14B8A6',
    dark: '#0F172A',
    light: '#F8FAFC',
    overlay: 'rgba(15, 23, 42, 0.85)',
  },
  typography: {
    fontFamily: 'Inter, system-ui, sans-serif',
    sizes: {
      heading: 48,
      subheading: 24,
      body: 16,
    },
  },
  spacing: {
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
  },
  animation: {
    springConfig: {
      damping: 12,
      stiffness: 200,
      mass: 0.5,
    },
  },
};

What does this code do?

This theme file is a single source of truth for all visual properties in your animation.

  • colors defines your brand palette. The overlay value is a semi-transparent dark background for the bar.
  • typography sets font family and sizes so all text is consistent.
  • spacing provides padding values at different scales.
  • animation.springConfig controls how the spring animation feels. Higher stiffness makes it snappier. Higher damping reduces oscillation.
  • Save src/theme.ts.

You won't see a change in Remotion Studio yet. The next file will use these tokens.

  • Create the animation component by running:
touch src/LowerThird.tsx
  • Open src/LowerThird.tsx and paste the following code:
import React from 'react';
import {
  AbsoluteFill,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from 'remotion';
import {theme} from './theme';

type LowerThirdProps = {
  name?: string;
  title?: string;
  accentColor?: string;
};

export const LowerThird: React.FC<LowerThirdProps> = ({
  name = 'Alex Johnson',
  title = 'Creative Director',
  accentColor = theme.colors.primary,
}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  // Spring animation drives the slide-in motion
  const slideIn = spring({
    frame,
    fps,
    config: theme.animation.springConfig,
  });

  // Map the spring value (0 to 1) to a horizontal position
  const translateX = interpolate(slideIn, [0, 1], [-400, 0]);

  // Fade out during the last 20 frames of the composition
  const fadeOut = interpolate(frame, [120, 140], [1, 0], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  return (
    <AbsoluteFill>
      <div
        style={{
          position: 'absolute',
          bottom: 80,
          left: 60,
          transform: `translateX(${translateX}px)`,
          opacity: fadeOut,
          display: 'flex',
          flexDirection: 'row',
          alignItems: 'stretch',
        }}
      >
        {/* Colored accent stripe on the left edge */}
        <div
          style={{
            width: 4,
            backgroundColor: accentColor,
            borderRadius: 2,
          }}
        />
        {/* Main content panel */}
        <div
          style={{
            backgroundColor: theme.colors.overlay,
            padding: `${theme.spacing.md}px ${theme.spacing.lg}px`,
            borderRadius: `0 ${theme.spacing.sm}px ${theme.spacing.sm}px 0`,
          }}
        >
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.heading,
              fontWeight: 700,
              color: theme.colors.light,
              lineHeight: 1.2,
            }}
          >
            {name}
          </div>
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.subheading,
              fontWeight: 400,
              color: accentColor,
              marginTop: theme.spacing.sm,
            }}
          >
            {title}
          </div>
        </div>
      </div>
    </AbsoluteFill>
  );
};

What does this component do?

This is a React component that Remotion renders frame-by-frame into a video.

  • useCurrentFrame() tells the component which frame is being rendered right now.
  • spring() generates a value from 0 to 1 with realistic physics. The spring config from your theme controls the feel.
  • interpolate() maps that 0-to-1 value into a pixel offset (-400 to 0). This creates the slide-in effect.
  • The second interpolate() handles the fade-out. Between frames 120 and 140, opacity goes from 1 to 0.
  • AbsoluteFill is a full-viewport container that Remotion uses for layering.
  • Save src/LowerThird.tsx.

You won't see a change in Remotion Studio yet. The next chunk registers this component as a composition.

  • Open src/Root.tsx and replace its entire contents with this code:
import React from 'react';
import {Composition} from 'remotion';
import {LowerThird} from './LowerThird';

// Register all compositions for Remotion Studio and rendering
export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="LowerThird"
        component={LowerThird}
        durationInFrames={150}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{
          name: 'Alex Johnson',
          title: 'Creative Director',
        }}
      />
    </>
  );
};

What does this code do?

  • Composition registers a video with Remotion. It defines the dimensions (1920x1080), frame rate (30fps), and duration (150 frames = 5 seconds).
  • defaultProps provides the default values that appear in the animation when you preview it.
  • The id is the name you'll use when rendering the video later.
  • Save src/Root.tsx.

Seeing a red error in Remotion Studio?

Check that the import path in LowerThird.tsx says ./theme (not ./theme.ts). Also make sure Root.tsx imports from ./LowerThird (not ./LowerThird.tsx).

help me fix my Remotion import errors

Preview your animation in Remotion Studio

Remotion Studio hot-reloads when you save files. Your new composition should already be visible.

  • Switch to the browser tab running Remotion Studio at http://localhost:3000.
  • Look in the left sidebar. You should see LowerThird listed as a composition.
  • Click LowerThird in the sidebar to select it.
  • Press the play button (or press Space) to watch the animation.

You should see a dark bar with an indigo accent stripe slide in from the left near the bottom of the frame. It displays "Alex Johnson" in white and "Creative Director" in indigo, then fades out near the end.

Don't see LowerThird in the sidebar?

Make sure you saved all three files (theme.ts, LowerThird.tsx, Root.tsx). Remotion Studio needs all imports to resolve before it can display the composition.

If the terminal running npm run dev shows errors, check that your file names match exactly (case-sensitive).

help me get my LowerThird composition to show up in Remotion Studio

✔️ Awesome, I've got everything!

Great. Double check you've saved all three files before moving on.

ⓧ I'd like to double check the full code

Here are all three files as they should look right now.

export const theme = {
  colors: {
    primary: '#6366F1',
    secondary: '#EC4899',
    accent: '#14B8A6',
    dark: '#0F172A',
    light: '#F8FAFC',
    overlay: 'rgba(15, 23, 42, 0.85)',
  },
  typography: {
    fontFamily: 'Inter, system-ui, sans-serif',
    sizes: {
      heading: 48,
      subheading: 24,
      body: 16,
    },
  },
  spacing: {
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
  },
  animation: {
    springConfig: {
      damping: 12,
      stiffness: 200,
      mass: 0.5,
    },
  },
};
import React from 'react';
import {
  AbsoluteFill,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from 'remotion';
import {theme} from './theme';

type LowerThirdProps = {
  name?: string;
  title?: string;
  accentColor?: string;
};

export const LowerThird: React.FC<LowerThirdProps> = ({
  name = 'Alex Johnson',
  title = 'Creative Director',
  accentColor = theme.colors.primary,
}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  const slideIn = spring({
    frame,
    fps,
    config: theme.animation.springConfig,
  });

  const translateX = interpolate(slideIn, [0, 1], [-400, 0]);

  const fadeOut = interpolate(frame, [120, 140], [1, 0], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  return (
    <AbsoluteFill>
      <div
        style={{
          position: 'absolute',
          bottom: 80,
          left: 60,
          transform: `translateX(${translateX}px)`,
          opacity: fadeOut,
          display: 'flex',
          flexDirection: 'row',
          alignItems: 'stretch',
        }}
      >
        <div
          style={{
            width: 4,
            backgroundColor: accentColor,
            borderRadius: 2,
          }}
        />
        <div
          style={{
            backgroundColor: theme.colors.overlay,
            padding: `${theme.spacing.md}px ${theme.spacing.lg}px`,
            borderRadius: `0 ${theme.spacing.sm}px ${theme.spacing.sm}px 0`,
          }}
        >
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.heading,
              fontWeight: 700,
              color: theme.colors.light,
              lineHeight: 1.2,
            }}
          >
            {name}
          </div>
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.subheading,
              fontWeight: 400,
              color: accentColor,
              marginTop: theme.spacing.sm,
            }}
          >
            {title}
          </div>
        </div>
      </div>
    </AbsoluteFill>
  );
};
import React from 'react';
import {Composition} from 'remotion';
import {LowerThird} from './LowerThird';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="LowerThird"
        component={LowerThird}
        durationInFrames={150}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{
          name: 'Alex Johnson',
          title: 'Creative Director',
        }}
      />
    </>
  );
};

Your first animation is alive. Next up, you'll explore the design system that powers all those colors and sizes, and learn how to customize it for your own brand.

Build a Design System

Your lower-third animation is working, but right now the visual values live directly inside the component.

A design system centralizes colors, fonts, spacing, and animation settings into a single file. This means you can change your entire brand look by editing one place, instead of hunting through every component.

In this step, get ready to:
  • Create a theme file with color, typography, spacing, and animation tokens.
  • Connect the theme tokens to your overlay component.
  • Verify that changing a theme value instantly updates the animation.

Claude Code

Your LowerThird component already references a theme file. Let's create that file using Claude Code.

  • Open a separate terminal in your project directory and run:
claude
  • Give Claude Code this prompt:
Create a theme.ts file in src/ that exports a theme object with these tokens: colors (primary: #6366F1, secondary: #EC4899, accent: #14B8A6, dark: #0F172A, light: #F8FAFC, overlay: rgba(15,23,42,0.85)), typography (fontFamily using Inter/system-ui/sans-serif, sizes for heading at 48px, subheading at 24px, and body at 16px), spacing (sm: 8, md: 16, lg: 24, xl: 32), and animation (springConfig with damping: 12, stiffness: 200, mass: 0.5).

What does this prompt do?

Claude Code creates a centralized theme file that your LowerThird component already imports. Once this file exists, all the theme references in your component resolve and the animation renders with your chosen design tokens.

  • After Claude Code generates the file, check Remotion Studio in your browser.
  • Play the LowerThird composition. The overlay should animate in with the indigo accent stripe and dark background panel.

Try changing a color in the generated theme file (like swapping the primary color) to verify that the animation updates instantly.

Manual

Create the theme file

Your LowerThird component already imports from a ./theme file. Creating this file with the right exports makes those references resolve.

  • Create a new file called src/theme.ts and paste the following code:
export const theme = {
  colors: {
    primary: '#6366F1',
    secondary: '#EC4899',
    accent: '#14B8A6',
    dark: '#0F172A',
    light: '#F8FAFC',
    overlay: 'rgba(15, 23, 42, 0.85)',
  },
  typography: {
    fontFamily: 'Inter, system-ui, sans-serif',
    sizes: {
      heading: 48,
      subheading: 24,
      body: 16,
    },
  },
  spacing: {
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
  },
  animation: {
    springConfig: {
      damping: 12,
      stiffness: 200,
      mass: 0.5,
    },
  },
};

What does this code do?

This file exports a single theme object that acts as the single source of truth for your project's visual style.

  • colors defines your palette. Indigo primary, pink secondary, teal accent, plus dark/light neutrals and a semi-transparent overlay background.
  • typography sets the font stack and size scale for headings, subheadings, and body text.
  • spacing provides consistent padding and margin values across components.
  • animation.springConfig controls how the spring physics feel. Higher stiffness means snappier motion. Higher damping means less bounce.
  • Save src/theme.ts.

Seeing a compile error?

Make sure your file is named exactly theme.ts (not theme.tsx) and placed inside the src/ folder. The import in LowerThird.tsx expects ./theme which resolves to src/theme.ts.

help me fix a compile error with my theme.ts file in Remotion

Understand how the theme connects to your component

Your LowerThird.tsx component already imports and uses these theme tokens throughout its code.

How does the theme connect to the component?

Open src/LowerThird.tsx and notice how theme tokens replace what would otherwise be hardcoded values:

  • theme.colors.primary sets the default accent color for the stripe and subtitle text.
  • theme.animation.springConfig feeds directly into the spring() function, controlling how the slide-in feels.
  • theme.colors.overlay and theme.colors.light control the background panel and text colors.
  • theme.spacing.md and theme.spacing.lg set consistent padding around the text.
  • theme.typography.fontFamily and theme.typography.sizes control the text appearance.
Preview the themed overlay
  • Switch to Remotion Studio in your browser.
  • Click the LowerThird composition in the sidebar.
  • Press the play button to watch the animation.

You should see the overlay slide in from the left with an indigo accent stripe, white name text, and the title in indigo. The bar fades out in the final frames.

  • To prove the design system works, open src/theme.ts and change the primary color from #6366F1 to #EC4899 (pink).
  • Save the file and check Remotion Studio. The accent stripe and subtitle text should now appear pink.
  • Change the color back to #6366F1 and save again.

Why use a design system?

Without a theme file, you would need to find and replace color values across every component whenever your brand changes. With this setup, one edit to theme.ts propagates everywhere instantly.

Color not updating in the preview?

Make sure you saved both theme.ts and check that Remotion Studio has hot-reloaded (the preview should update automatically). If it hasn't, try refreshing the browser tab.

help me debug why my Remotion theme changes aren't showing up

✔️ Awesome, I've got everything!

Great. Double check you've saved src/theme.ts and that your animation plays correctly in Remotion Studio.

ⓧ I'd like to double check the full code

Here is the complete code for all files in your project so far.

export const theme = {
  colors: {
    primary: '#6366F1',
    secondary: '#EC4899',
    accent: '#14B8A6',
    dark: '#0F172A',
    light: '#F8FAFC',
    overlay: 'rgba(15, 23, 42, 0.85)',
  },
  typography: {
    fontFamily: 'Inter, system-ui, sans-serif',
    sizes: {
      heading: 48,
      subheading: 24,
      body: 16,
    },
  },
  spacing: {
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
  },
  animation: {
    springConfig: {
      damping: 12,
      stiffness: 200,
      mass: 0.5,
    },
  },
};
import React from 'react';
import {
  AbsoluteFill,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from 'remotion';
import {theme} from './theme';

type LowerThirdProps = {
  name?: string;
  title?: string;
  accentColor?: string;
};

export const LowerThird: React.FC<LowerThirdProps> = ({
  name = 'Alex Johnson',
  title = 'Creative Director',
  accentColor = theme.colors.primary,
}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  const slideIn = spring({
    frame,
    fps,
    config: theme.animation.springConfig,
  });

  const translateX = interpolate(slideIn, [0, 1], [-400, 0]);

  const fadeOut = interpolate(frame, [120, 140], [1, 0], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  return (
    <AbsoluteFill>
      <div
        style={{
          position: 'absolute',
          bottom: 80,
          left: 60,
          transform: `translateX(${translateX}px)`,
          opacity: fadeOut,
          display: 'flex',
          flexDirection: 'row',
          alignItems: 'stretch',
        }}
      >
        <div
          style={{
            width: 4,
            backgroundColor: accentColor,
            borderRadius: 2,
          }}
        />
        <div
          style={{
            backgroundColor: theme.colors.overlay,
            padding: `${theme.spacing.md}px ${theme.spacing.lg}px`,
            borderRadius: `0 ${theme.spacing.sm}px ${theme.spacing.sm}px 0`,
          }}
        >
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.heading,
              fontWeight: 700,
              color: theme.colors.light,
              lineHeight: 1.2,
            }}
          >
            {name}
          </div>
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.subheading,
              fontWeight: 400,
              color: accentColor,
              marginTop: theme.spacing.sm,
            }}
          >
            {title}
          </div>
        </div>
      </div>
    </AbsoluteFill>
  );
};
import React from 'react';
import {Composition} from 'remotion';
import {LowerThird} from './LowerThird';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="LowerThird"
        component={LowerThird}
        durationInFrames={150}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{
          name: 'Alex Johnson',
          title: 'Creative Director',
        }}
      />
    </>
  );
};

Your overlay is now powered by a proper design system. Next up, you'll render the final animation as an MP4 file you can share anywhere.

Render the Final Video

Your animation is looking great in Remotion Studio. Now it's time to export it as a real video file you can share, upload, or use in other projects.

Remotion gives you two ways to render: a visual interface inside Remotion Studio, or a CLI command for quick exports. Both produce the same H.264 MP4 output.

In this step, get ready to:
  • Render your LowerThird composition as an MP4 file.
  • Verify the output video plays correctly with the spring animation and fade-out.
Render the animation

Choose whichever rendering method you prefer. Both produce the same MP4 output in the out/ folder.

Remotion Studio

  • In Remotion Studio, click the Render button in the top right corner.
  • Select the LowerThird composition from the dropdown.
  • Leave the codec set to H.264 (the default).
  • Click Render video.

The render progress bar fills as Remotion processes each frame. When complete, the output appears in your project's out/ folder.

What does Remotion Studio do during rendering?

Remotion renders each frame individually as an image, then stitches them together into an MP4 using FFmpeg under the hood. For 150 frames at 1920x1080, this typically takes 10-30 seconds depending on your machine.

CLI

  • Open a new terminal window in your overlay-project folder.
  • Render the LowerThird composition to an MP4 by running this command:
npx remotion render LowerThird out/lower-third.mp4

What does this command do?

  • npx remotion render invokes Remotion's render engine from the command line.
  • LowerThird is the composition ID you registered in Root.tsx.
  • out/lower-third.mp4 is the output path. Remotion defaults to H.264 codec for .mp4 files.

You should see a progress indicator in the terminal as each frame renders. The render typically completes in 10-30 seconds.

Seeing a 'composition not found' error?

  • Make sure you typed LowerThird exactly as written (capital L, capital T, no spaces). This must match the id prop in your Composition registration.
  • Check that src/Root.tsx is saved and imports LowerThird correctly.

Help me fix a 'composition not found' error when rendering with Remotion CLI.

Verify the output
  • Confirm the file exists by running:
ls out/
  • You should see lower-third.mp4 listed in the output.
  • Open out/lower-third.mp4 in any video player on your computer.

You should see a 5-second video at 1920x1080. The lower-third bar slides in from the left with a spring animation, displays "Alex Johnson" and "Creative Director", then fades out near the end.

Video looks blank or animation is missing?

  • If the video is entirely black, check that your LowerThird.tsx file is saved and contains the component export.
  • If the overlay appears but doesn't animate, verify that theme.ts exports the animation.springConfig object correctly.
  • Try previewing the composition in Remotion Studio first. If it looks correct there, the render command should produce the same result.

My rendered Remotion video looks wrong. Help me debug.

✔️ Awesome, I've got everything!

Great. Double check you've saved all your files before moving on.

ⓧ I'd like to double check the full code

Here are all three files in their final state. Your code should match these exactly.

export const theme = {
  colors: {
    primary: '#6366F1',
    secondary: '#EC4899',
    accent: '#14B8A6',
    dark: '#0F172A',
    light: '#F8FAFC',
    overlay: 'rgba(15, 23, 42, 0.85)',
  },
  typography: {
    fontFamily: 'Inter, system-ui, sans-serif',
    sizes: {
      heading: 48,
      subheading: 24,
      body: 16,
    },
  },
  spacing: {
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
  },
  animation: {
    springConfig: {
      damping: 12,
      stiffness: 200,
      mass: 0.5,
    },
  },
};
import React from 'react';
import {
  AbsoluteFill,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from 'remotion';
import {theme} from './theme';

type LowerThirdProps = {
  name?: string;
  title?: string;
  accentColor?: string;
};

export const LowerThird: React.FC<LowerThirdProps> = ({
  name = 'Alex Johnson',
  title = 'Creative Director',
  accentColor = theme.colors.primary,
}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  const slideIn = spring({
    frame,
    fps,
    config: theme.animation.springConfig,
  });

  const translateX = interpolate(slideIn, [0, 1], [-400, 0]);

  const fadeOut = interpolate(frame, [120, 140], [1, 0], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  return (
    <AbsoluteFill>
      <div
        style={{
          position: 'absolute',
          bottom: 80,
          left: 60,
          transform: `translateX(${translateX}px)`,
          opacity: fadeOut,
          display: 'flex',
          flexDirection: 'row',
          alignItems: 'stretch',
        }}
      >
        <div
          style={{
            width: 4,
            backgroundColor: accentColor,
            borderRadius: 2,
          }}
        />
        <div
          style={{
            backgroundColor: theme.colors.overlay,
            padding: `${theme.spacing.md}px ${theme.spacing.lg}px`,
            borderRadius: `0 ${theme.spacing.sm}px ${theme.spacing.sm}px 0`,
          }}
        >
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.heading,
              fontWeight: 700,
              color: theme.colors.light,
              lineHeight: 1.2,
            }}
          >
            {name}
          </div>
          <div
            style={{
              fontFamily: theme.typography.fontFamily,
              fontSize: theme.typography.sizes.subheading,
              fontWeight: 400,
              color: accentColor,
              marginTop: theme.spacing.sm,
            }}
          >
            {title}
          </div>
        </div>
      </div>
    </AbsoluteFill>
  );
};
import React from 'react';
import {Composition} from 'remotion';
import {LowerThird} from './LowerThird';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="LowerThird"
        component={LowerThird}
        durationInFrames={150}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{
          name: 'Alex Johnson',
          title: 'Creative Director',
        }}
      />
    </>
  );
};

Secret mission

Composite the Overlay on Video

Your overlay looks great on its own, but the real power of Remotion is compositing layers together. In this secret mission, you will layer your animated lower-third on top of real video footage using Remotion's built-in compositing system.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

This project runs entirely locally, so there are no ongoing costs. Remotion is free for individuals and teams up to 3 people.

Resources you used:

  • Node.js installation.
  • The overlay-project folder on your Desktop.
  • Remotion Studio (local dev server).
  • Rendered MP4 files in the out/ folder.
  • Claude Code (optional, requires Anthropic subscription).

Keep everything running

No action needed. Choose this if you're still actively building or want to experiment with new animations and design system tokens.

  • Your overlay-project folder stays on your Desktop.
  • Run npm run dev anytime to reopen Remotion Studio.
  • Node.js remains installed for future projects.

Pause - I'll come back to this later

Shut down running processes to free up memory. Your project files stay intact for next time.

  • Stop Remotion Studio by pressing Ctrl+C in the terminal where it is running.
  • If you launched Claude Code, type /exit to close the session.
  • Your project folder and rendered videos remain on your Desktop.

Delete - I don't want to use this again

Remove all project resources and start fresh. This cannot be undone.

  • Stop Remotion Studio by pressing Ctrl+C in the terminal.
  • Delete the entire overlay-project folder from your Desktop.
  • Optionally uninstall Node.js if you do not need it for other projects.
  • If you subscribed to Anthropic for Claude Code, cancel your subscription in your Anthropic account settings.

That's a Wrap!

That's a Wrap!

Nice work. You just built animated video overlays entirely with code, no After Effects license needed.

You've learned how to:

  • Built a spring-animated lower-third overlay using Remotion's interpolate and spring functions.
  • Created a reusable design system with color, typography, spacing, and animation tokens that power the entire overlay.
  • Rendered the final animation as a production-ready MP4 using the Remotion CLI.
  • Secret Mission: Composited the overlay on top of real video footage using AbsoluteFill layers and OffthreadVideo.

Ready to quiz yourself?