Introduction: What you’ll build (and why it matters)
You’re going to build a practical, repeatable “Video IDE” workflow: describe a video scene in natural language, let a coding agent generate (or modify) Remotion React components, and preview the result instantly in Remotion Studio. Instead of editing a timeline, you’ll iterate like a developer: prompt → code → live preview → refine → render.
This is valuable because Remotion’s AI-oriented workflow is now explicit and actionable: you can add “agent skills” to your repo so your coding agent learns Remotion-specific patterns (frame-based animation, asset loading, composition wiring). That removes the usual friction where an LLM writes “almost correct” video code that doesn’t actually render.
By the end, you’ll have:
- A Remotion project set up for AI-assisted development
- A reusable “prompt contract” that gets you working components reliably
- Two example scenes (kinetic title + lower third) built via agent prompts
- A render pipeline that exports MP4 from the CLI (and what output to expect)
Prerequisites
Tools you must install
- Node.js (LTS recommended) and npm
- A code editor or agent: Cursor, Claude Code, Codex-based tooling, OpenCode, etc.
- Remotion (installed into the project via the starter)
Knowledge you should have
- Basic React (components, props, CSS)
- Basic command line usage
Hardware note
Video rendering can be CPU-heavy. You can still follow this tutorial on modest hardware, but expect slower exports for longer or more complex compositions.
Step 1 — Create a Remotion project with agent skills
Do this to start from a clean baseline that Remotion Studio can preview immediately, while also adding AI-readable guidance (“skills”) to your repo.
1.1 Generate a blank project
npx create-video@latest --blankWhen prompted:
- Choose a project name (example:
ai-video-ide) - Enable agent skills when asked (this is the key to AI reliability)
- Optional: enable Tailwind CSS if you want quick styling primitives
Why this step is necessary: the starter wires up Remotion Studio, composition registration, and dev scripts correctly. The agent skills provide Remotion-specific constraints and patterns so your coding agent generates code that actually runs in the Remotion renderer.
1.2 Install dependencies and start Remotion Studio
cd ai-video-ide
npm install
npm run devExpected result
- Your browser opens Remotion Studio (usually at
http://localhost:3000) - You see at least one default composition in the left sidebar
Screenshot description: A web app titled “Remotion Studio” showing a left sidebar with compositions, a central preview canvas, and transport controls (playhead, play/pause, timeline).
Step 2 — Understand the “Video IDE” loop (prompt → code → preview → refine)
Do this before generating anything: adopt a strict iteration loop that prevents you from accumulating broken code.
The loop
- Prompt the agent for a single component or small change.
- Run (or keep running)
npm run devso Studio updates. - Preview the composition and scrub frames to validate animation.
- Refine by asking the agent for incremental edits only.
- Render to MP4 only after preview is correct.
Why this matters: Remotion is frame-based. A component can “look okay” at frame 0 but fail at frame 120 due to math, opacity bounds, or missing assets. Scrubbing the timeline catches these errors immediately.
Step 3 — Add a composition registry pattern the agent can’t mess up
Do this to make your project predictable: keep compositions in one place, keep scene components in another. AI agents are more successful when you give them a stable file map.
3.1 Create a folder structure
mkdir -p src/compositions src/scenes src/lib3.2 Create a simple shared helper for animation clamping
Create src/lib/clamp.ts:
export const clamp = (value: number, min: number, max: number) => {
return Math.min(max, Math.max(min, value));
};
Why this step is necessary: LLMs often generate opacity/scale values that overshoot (negative opacity, scale < 0). Clamping prevents flicker and invalid CSS values.
3.3 Create your main composition entry
Locate your Remotion root file (commonly src/Root.tsx in Remotion starters). Replace (or adjust) it to register two compositions: a title and a lower third.
Create/modify src/Root.tsx:
import React from 'react';
import {Composition} from 'remotion';
import {KineticTitle} from './compositions/KineticTitle';
import {LowerThird} from './compositions/LowerThird';
export const RemotionRoot: React.FC = () => {
return (
<>
<Composition
id="KineticTitle"
component={KineticTitle}
durationInFrames={150}
fps={30}
width={1920}
height={1080}
defaultProps={{
title: 'Build Videos Like Software',
subtitle: 'AI-assisted Remotion workflow',
}}
/>
<Composition
id="LowerThird"
component={LowerThird}
durationInFrames={240}
fps={30}
width={1920}
height={1080}
defaultProps={{
name: 'Alex Developer',
role: 'AI Video Engineer',
accent: '#7C3AED',
}}
/>
</>
);
};
Expected result
- Remotion Studio now shows
KineticTitleandLowerThirdin the sidebar - If your dev server is still running, Studio hot-reloads automatically
Step 4 — Use a coding agent correctly: give it a strict prompt contract
Do this to get consistent, runnable output. You are not just “asking for an animation”; you are instructing an agent to edit a codebase with constraints.
4.1 Use this prompt template (copy/paste)
Task: Create or modify a Remotion component.
Constraints:
- Must be valid TypeScript React.
- Must not break existing compositions in src/Root.tsx.
- Use Remotion hooks (useCurrentFrame, useVideoConfig) for animation.
- Clamp opacity/scale to safe ranges.
- Keep all logic deterministic (no Math.random at render time).
- Use inline styles or Tailwind, but be consistent.
Output:
- Tell me exactly which files to create/modify.
- Provide full file contents for new files.
- Provide minimal diffs for existing files.
- Include how to preview it in Remotion Studio.
Why this step is necessary: without explicit constraints, agents often introduce non-determinism, reference missing files, or change composition IDs. Those failures are time-wasters in a render pipeline.
4.2 If your agent supports repo “skills”, enable them
If you selected “agent skills” during project creation, your repo will include instruction files meant for agents. Configure your agent to read them (varies by tool):
- In AI-first editors, ensure “project instructions” are enabled.
- In CLI agents, point it at the repo root and confirm it reads instruction files.
Expected result: the agent will consistently use Remotion primitives like interpolate, useCurrentFrame(), and proper component patterns, instead of generic web animations.
Step 5 — Generate your first AI-built scene: a kinetic title composition
Do this to validate the full flow: agent-generated code + Studio preview + frame-accurate animation.
5.1 Ask the agent to create the composition component
Prompt your agent:
Task: Create src/compositions/KineticTitle.tsx.
Behavior:
- Fullscreen background with a subtle gradient.
- Title animates in: slide up + fade in over frames 0-25.
- Subtitle animates in: slight delay (frames 10-40) with smaller movement.
- Add a soft “accent bar” behind the title that expands horizontally.
Props:
- title: string
- subtitle: string
Implementation:
- Use useCurrentFrame and useVideoConfig.
- Use interpolate and clamp values.
- Ensure animations look good at 30fps, 150 frames.
Output: full file contents.
5.2 Use this tested implementation (if you want a known-good baseline)
Create src/compositions/KineticTitle.tsx:
import React from 'react';
import {interpolate, useCurrentFrame, useVideoConfig} from 'remotion';
import {clamp} from '../lib/clamp';
export type KineticTitleProps = {
title: string;
subtitle: string;
};
export const KineticTitle: React.FC<KineticTitleProps> = ({title, subtitle}) => {
const frame = useCurrentFrame();
const {width, height, fps} = useVideoConfig();
const titleProgressRaw = interpolate(frame, [0, 25], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const titleProgress = clamp(titleProgressRaw, 0, 1);
const subtitleProgressRaw = interpolate(frame, [10, 40], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const subtitleProgress = clamp(subtitleProgressRaw, 0, 1);
// Motion
const titleY = interpolate(titleProgress, [0, 1], [40, 0]);
const titleOpacity = titleProgress;
const subtitleY = interpolate(subtitleProgress, [0, 1], [20, 0]);
const subtitleOpacity = subtitleProgress;
// Accent bar expands quickly, then settles
const barProgressRaw = interpolate(frame, [0, 18], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const barProgress = clamp(barProgressRaw, 0, 1);
const barScaleX = interpolate(barProgress, [0, 1], [0.2, 1]);
const barOpacity = clamp(interpolate(barProgress, [0, 1], [0, 1]), 0, 1);
const bg = {
background: 'radial-gradient(circle at 20% 20%, #111827 0%, #030712 55%, #000 100%)',
} as const;
const safePadding = Math.round(width * 0.08);
return (
<div
style={{
...bg,
width,
height,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontFamily:
'-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif',
color: 'white',
}}
>
<div style={{width: '100%', padding: safePadding}}>
<div style={{position: 'relative', display: 'inline-block', marginBottom: 18}}>
<div
style={{
position: 'absolute',
left: -14,
right: -14,
top: '55%',
height: 22,
transformOrigin: 'left center',
transform: `translateY(-50%) scaleX(${barScaleX})`,
background: 'linear-gradient(90deg, rgba(124,58,237,0.95), rgba(59,130,246,0.85))',
filter: 'blur(0px)',
borderRadius: 999,
opacity: barOpacity,
}}
/>
<div
style={{
position: 'relative',
fontSize: 86,
fontWeight: 800,
lineHeight: 1.05,
letterSpacing: -1,
transform: `translateY(${titleY}px)`,
opacity: titleOpacity,
textShadow: '0 12px 40px rgba(0,0,0,0.55)',
}}
>
{title}
</div>
</div>
<div
style={{
fontSize: 34,
fontWeight: 500,
color: 'rgba(255,255,255,0.85)',
transform: `translateY(${subtitleY}px)`,
opacity: subtitleOpacity,
maxWidth: 1200,
}}
>
{subtitle}
</div>
<div style={{marginTop: 26, color: 'rgba(255,255,255,0.45)', fontSize: 18}}>
<span>Tip: Scrub the timeline to verify animation at frames 0, 10, 25, 40.</span>
<span style={{marginLeft: 12}}>FPS: {fps}</span>
</div>
</div>
</div>
);
};
Expected result
- In Remotion Studio, select
KineticTitle - At frame 0: title mostly invisible and lower
- By frame ~25: title fully visible and in place, accent bar expanded
- By frame ~40: subtitle fully visible
Screenshot description: A dark, cinematic gradient background. Large bold title centered-left with a purple-to-blue pill-shaped bar behind it. Subtitle below in smaller text.
Step 6 — Generate a second component: a reusable lower-third (and make it agent-friendly)
Do this to prove you can build modular “video UI components” the same way you build web UI: with props, predictable timing, and reusable styling.
6.1 Ask the agent for a lower third
Prompt:
Task: Create src/compositions/LowerThird.tsx.
Behavior:
- Lower third slides in from the left between frames 0-20.
- Stays on screen, then slides out between frames 180-210.
- Includes name (large) and role (smaller).
- Accent color prop for a vertical bar.
Props:
- name: string
- role: string
- accent: string
Implementation:
- Use useCurrentFrame/useVideoConfig and interpolate.
- Clamp transforms and opacity.
Output: full file contents.
6.2 Use this tested implementation
Create src/compositions/LowerThird.tsx:
import React from 'react';
import {interpolate, useCurrentFrame, useVideoConfig} from 'remotion';
import {clamp} from '../lib/clamp';
export type LowerThirdProps = {
name: string;
role: string;
accent: string;
};
export const LowerThird: React.FC<LowerThirdProps> = ({name, role, accent}) => {
const frame = useCurrentFrame();
const {width, height} = useVideoConfig();
// In and out windows
const inP = clamp(
interpolate(frame, [0, 20], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}),
0,
1
);
const outP = clamp(
interpolate(frame, [180, 210], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}),
0,
1
);
// Combine: fully visible after in, then fades/moves out
const visible = clamp(inP * (1 - outP), 0, 1);
const xIn = interpolate(inP, [0, 1], [-520, 0]);
const xOut = interpolate(outP, [0, 1], [0, -520]);
const x = xIn + xOut;
const y = height - 220;
return (
<div
style={{
width,
height,
background: 'linear-gradient(180deg, #0b1220, #020617)',
fontFamily:
'-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif',
}}
>
<div
style={{
position: 'absolute',
left: 80,
top: y,
transform: `translateX(${x}px)`,
opacity: visible,
display: 'flex',
alignItems: 'stretch',
borderRadius: 18,
overflow: 'hidden',
boxShadow: '0 18px 60px rgba(0,0,0,0.45)',
background: 'rgba(17, 24, 39, 0.82)',
backdropFilter: 'blur(10px)',
}}
>
<div style={{width: 10, background: accent}} />
<div style={{padding: '18px 22px 18px 18px', minWidth: 520}}>
<div style={{color: 'white', fontSize: 44, fontWeight: 800, letterSpacing: -0.5}}>
{name}
</div>
<div style={{color: 'rgba(255,255,255,0.72)', fontSize: 22, fontWeight: 500, marginTop: 6}}>
{role}
</div>
</div>
</div>
</div>
);
};
Expected result
- Frame 0: lower third off-screen left
- Frame ~20: lower third fully visible
- Frames 20–180: stable (no jitter)
- Frame ~210: lower third has slid out left
Step 7 — Preview like a pro in Remotion Studio (scrub, switch props, validate frames)
Do this to catch timing and composition errors early, before exporting.
7.1 Scrub key frames
In Studio:
- Select
KineticTitle, scrub to frames0,10,25,40. - Select
LowerThird, scrub to frames0,20,180,210.
Why this step is necessary: Remotion renders each frame. If an animation overshoots or uses invalid values, it often shows up at boundaries.
7.2 Edit props live (fast iteration)
Use Studio’s props editor (if visible) or change defaultProps in src/Root.tsx.
Example change:
defaultProps={{
name: 'Sam Product',
role: 'Remotion + AI Workflow',
accent: '#22C55E',
}}
Expected result
Studio hot-reloads and the accent bar color changes, with no rebuild steps beyond saving the file.
Step 8 — Render to MP4 from the CLI (and know what to expect)
Do this once previews look correct. Rendering is where you’ll discover missing assets, non-determinism, or performance issues.
8.1 Render the kinetic title composition
npx remotion render KineticTitle out/kinetic-title.mp48.2 Render the lower third composition
npx remotion render LowerThird out/lower-third.mp4Why this step is necessary: Studio preview is interactive and forgiving; export is the production path. You need a successful CLI render to validate that the result is actually shippable.
Expected output
- An
out/folder created (if it doesn’t exist, Remotion typically creates it) - Two MP4 files written:
out/kinetic-title.mp4
out/lower-third.mp4You’ll also see terminal logs showing render progress by frame. If rendering fails, jump to Troubleshooting below.
Step 9 — Make the workflow truly “IDE-like”: use small, safe agent iterations
Do this to avoid the most common failure mode: the agent rewrites large sections and introduces new errors.
9.1 Use micro-prompts (examples)
Instead of “make it cooler,” say:
- “In
KineticTitle.tsx, reduce subtitle font from 34 to 30 and increase line-height slightly.” - “Change the accent bar behind the title to be 16px tall instead of 22px.”
- “Add a subtle vignette overlay using a pseudo-layer div with
mixBlendModeoff.”
9.2 Ask for diffs, not rewrites
Prompt:
Only output a minimal diff for src/compositions/KineticTitle.tsx. Do not rewrite the entire file.Why this works: diffs constrain the agent’s scope and reduce accidental breakage of imports, props, or composition IDs.
Troubleshooting: common issues and fixes
Issue 1: Remotion Studio doesn’t open / blank page
- Cause: dev server not running or port conflict.
- Fix: run
npm run devagain and read the printed URL. - Fix (port conflict): stop the conflicting process or configure a different port if your setup supports it.
Issue 2: “Composition not found” when rendering
- Cause: you rendered with an ID that doesn’t match
src/Root.tsx. - Fix: verify IDs:
KineticTitleandLowerThirdmust match exactly (case-sensitive). - Check: in Studio’s left sidebar, copy the exact composition name.
Issue 3: TypeScript errors after agent edits
- Cause: missing imports (common with
interpolate, hooks, or helper functions). - Fix: ensure imports exist at top of the file, and that file paths are correct.
Issue 4: Animation flickers or disappears mid-way
- Cause: opacity or scale goes out of range.
- Fix: clamp values and use
extrapolate: 'clamp'oninterpolate. - Fix: scrub boundary frames (0/last frame and in/out transition frames) to confirm stability.
Issue 5: Render is slow
- Cause: heavy effects (blur, big shadows, large layers) or complex React trees per frame.
- Fix: reduce expensive CSS (especially large-area blur), simplify layers, avoid unnecessary re-renders.
- Fix: ask the agent to optimize: “Reduce layers and avoid expensive filters.”
Issue 6: Agent generates non-deterministic code
- Cause: use of
Math.random()in render path. - Fix: replace randomness with seeded deterministic values, or precompute outside render (and keep stable per frame).
Warning: destructive actions to avoid
- Do not let an agent “refactor the whole project” unless you’ve committed changes. It can break composition IDs and dev scripts.
- Do not delete the Remotion root registration file (
src/Root.tsx) without replacing it; rendering depends on it.
Testing: verify the workflow end-to-end
Do this to confirm that your “Video IDE” loop works reliably.
Test 1 — Hot reload and preview
- With Studio open, change
titletext insrc/Root.tsx. - Save the file.
Expected result: Studio updates the title immediately without restarting the dev server.
Test 2 — Frame-accurate transitions
- Set playhead to frame 0, confirm title opacity is near 0.
- Set playhead to frame 25, confirm title opacity is 1 and Y translation is 0.
Expected result: no flicker, no negative opacity, no layout jump.
Test 3 — CLI render output exists and is playable
- Run:
npx remotion render KineticTitle out/kinetic-title.mp4 - Open the MP4 in your system video player.
Expected result: the exported MP4 matches Studio preview visually.
Next steps: extend your AI-assisted Video IDE workflow
1) Add assets correctly (images, video, audio)
Ask your agent to use Remotion’s asset helpers (commonly staticFile()) and store files in the project’s public/static folder as recommended by your setup. Then verify assets load both in Studio and in CLI render.
2) Build a library of “scene primitives”
Create reusable components like:
SlideIn,FadeIn,PopwrappersBackgroundGrid,NoiseOverlay,GradientWashChartBar,Counter,LogoLockup
Then prompt the agent to compose scenes from primitives instead of generating new animations every time.
3) Parameterize for template-driven video generation
Move text/colors/timings into props, and drive them from JSON. This is the bridge from “one-off motion graphic” to “video generation system.”
4) Create a prompt-to-scene checklist
Require the agent to specify:
- Composition duration and fps assumptions
- Animation frame ranges
- Props schema and defaults
- Preview instructions (which composition to click)
Conclusion: you now have a working AI + Remotion “Video IDE”
You built an AI-assisted workflow that behaves like an IDE for video: you generate React video components with a coding agent, preview them instantly in Remotion Studio, iterate with tight diffs, and export MP4s from the CLI.
Keep the workflow robust by constraining agent scope, clamping animation values, previewing boundary frames, and rendering early/often. From here, scale up by building a component library and parameterized templates—so your agent is assembling reliable building blocks instead of inventing new patterns each time.

