Skills/Remotion Agent Skills

Remotion Agent Skills

byRemotion·video-editing

Agent Skills that define best practices for working in Remotion projects. Useful for AI agents like Claude Code, Codex, Kimi Code, or Cursor. Covers project creation (bun create video), React markup, animations, layout, typography, media elements, Studio live preview, map animations (Mapbox, MapLibre, CesiumJS), captions, and rendering pipelines.

CLAUDE CODECURSORCODEXKIMI CODEWINDSURF
Loading stats...

Installation

Install directly into your agent CLI.

$npx skills add remotion-dev/skills
Usage examplePrompt your agent

“Create a new Remotion video composition: configure FPS and dimensions, build animated React markup with spring physics, and launch Studio for live preview.”

Remotion Agent Skills Subgroups

Remotion Core & Project Creation (/remotion-create)jump ↓

Scaffold new Remotion projects (bun create video), register Compositions and Stills, configure resolution and frame rate, and structure multi-scene timelines.

React Video Markup, Transitions & Audio (/remotion-markup)jump ↓

Author animated title cards, spring physics interpolations, Google Fonts typography, audio visualizations, sound effects, and smooth scene transitions.

Studio Live Preview & Interactivity (/remotion-studio)jump ↓

Launch Remotion Studio (npx remotion studio) for real-time browser preview and configure interactive editable controls.

Programmatic Rendering & Pipelines (/remotion-render)jump ↓

Render compositions to MP4/WebM videos, export transparent alpha videos, or output high-resolution still frames locally or on Lambda.

Geographic Map Animations & 3D Flyovers (/remotion-maps)jump ↓

Animate GPS routes, markers, and camera-follow paths with Mapbox, MapLibre, MapTiler, GeoJSON, and 3D terrain flyovers with CesiumJS.

Captions, Multimedia & SaaS Architecture (/remotion-saas)jump ↓

Transcribe and render animated subtitles, embed interactive <Player> in web apps, and perform in-browser video manipulation with Mediabunny.

SKILL.md

--- name: remotion description: "Official Remotion skills for AI agents: React video markup, Studio preview, compositions, transitions, map animations, captions, and rendering." license: MIT metadata: author: Remotion repo: remotion-dev/skills version: "4.0.536" ---

Remotion Agent Skills

Remotion maintains a comprehensive set of Agent Skills that define best practices for programmatic video creation in React. Useful for AI agents like Claude Code, Codex, Kimi Code, or Cursor.

Overview & Architecture

  1. When creating videos with Remotion and AI agents:
  2. Creation: Scaffold projects with bun create video or npx create-video@latest, define compositions, and register FPS/dimensions.
  3. Interactive Preview: Launch Studio (npx remotion studio) immediately so users can watch changes and steer output in real time.
  4. React Markup: Use spring physics, interpolations, sequence layouts, audio visualization, Google Fonts, and transitions.
  5. Export: Render into MP4/WebM videos or still frames locally, via Docker, or serverless with Remotion Lambda.

Subgroups Included

1. Remotion Core & Project Creation (/remotion-create) — Scaffold new Remotion projects, register Compositions and Stills, configure resolution and frame rate, and structure multi-scene timelines. 2. React Video Markup, Transitions & Audio (/remotion-markup) — Author animated title cards, spring physics interpolations, Google Fonts typography, audio visualizations, sound effects, and smooth scene transitions. 3. Studio Live Preview & Interactivity (/remotion-studio) — Launch Remotion Studio (npx remotion studio) for real-time browser preview and configure interactive editable controls. 4. Programmatic Rendering & Pipelines (/remotion-render) — Render compositions to MP4/WebM videos, export transparent alpha videos, or output high-resolution still frames locally or on Lambda. 5. Geographic Map Animations & 3D Flyovers (/remotion-maps) — Animate GPS routes, markers, and camera-follow paths with Mapbox, MapLibre, MapTiler, GeoJSON, and 3D terrain flyovers with CesiumJS. 6. Captions, Multimedia & SaaS Architecture (/remotion-saas) — Transcribe and render animated subtitles, embed interactive <Player> in web apps, and perform in-browser video manipulation with Mediabunny.

Installation Quick Start

Any Agent (Skills CLI)

bash npx skills add remotion-dev/skills

New Project Setup

bash bun create video # or npx create-video@latest

Common Example Prompts

- "/remotion-create Make a promo video for a record store" - "/remotion-markup Create an animated title card using Inter" - "/remotion-studio Launch the Studio preview" - "/remotion-render Export this composition to an MP4 video" - "/remotion-maps Animate a route from Los Angeles to New York and make the camera follow it" - "/remotion-saas Turn my video into a SaaS with Player and Lambda rendering"


Subgroup 1: Remotion Core & Project Creation (/remotion-create)

Preserve user changes

Users may make edits in the code outside of the conversation.

If you detect a surprising change made in the meanwhile, don't overwrite it, assume it was intentional or ask for confirmation.

Creating a video

If the user asks to make, create, or build a new video or composition, load Create a new Remotion video, whether or not a Remotion project already exists.

New project setup

If no Remotion project currently exists, load Create a new Remotion project

React Markup Best Practices

If you are writing Remotion React Markup, load Remotion Markup Best Practices

Maps

For static maps, animated routes and markers, geographic explainers, Mapbox, MapLibre, MapTiler, GeoJSON, or 3D geographic flyovers, load Remotion Maps.

Multimedia

For achieving multimedia tasks in the browser, such as trimming, cropping videos, or getting metadata from them, load Remotion Multimedia

Improving Interactivity

By structuring the Remotion markup well, we can allow users to interactively change things in the Studio and write back to code. If relevant: Interactivity Best Practices

Open the preview

If the user asks to "make" a video, "create" a video, etc. Don't render the video by default unless they are very explicit. They want to instead see an interactive preview. As soon as the project can run, start Studio and open the preview in the browser before building or editing the composition. Keep it open while you work so the user can watch progress and steer.

If you are using Cursor

Run Studio without --no-open so it opens the browser automatically:

bash
npx remotion studio

If you are using another agent client with an in-app browser

You can use the command above to let Studio open the browser, or run:

bash
npx remotion studio --no-open

This will start a long-running process and print the server URL for the preview. If the server is already started, it will print the URL. If you use --no-open, open the exact printed URL in the in-app browser and verify that Studio loads. Once a composition exists, verify that its video preview loads. If you cannot open it there, run Studio without --no-open. You can visit a specific composition by navigating to /[composition-id], for example http://localhost:3000/MapAnimation.

:::note The Studio supports WebMCP tools. :::

If you do not have an in-app browser

This will open the Studio in the browser or refocus it if it is already open.

bash
npx remotion studio

More options

To launch a project in Remotion Studio, open its exact local URL, or configure Studio CLI flags, load Remotion Studio.

Render the video

Only render if the user is very explicit in asking for it. E.g. "Render the video", "Export", "Give me the MP4".

The preview also has a more intuitive rendering interface, so consider using it instead of the command line for rendering.

npx remotion render

For more options, see Rendering.

For advanced rendering beyond simple npx remotion render, see: Rendering Best Practices

Captions

When working with Captions, load Remotion Captions.

Creating a SaaS, automation or application

Use the Remotion SaaS skill for knowledge about Remotion-powered SaaS apps, such as <Player>, rendering on Lambda, Vercel, Cloudflare, via Express.js, client-side rendering, or for finding the right SaaS template.

Looking up Remotion APIs and documentation

To find and read current Remotion documentation, load Remotion Docs.

Upgrading

To upgrade Remotion, related packages, compatible Mediabunny packages, and installed Remotion Agent Skills, load Remotion Upgrade.


Project Scaffolding Guide

These are instructions for making a new Remotion project and composition. If this is not the next task, see Remotion Best Practices

Scaffold a project

If a project already exists, skip this. Ensure Node.js and Git is installed, and the current folder is appropriate for starting a new project.

Inspect the current folder, including hidden files, before choosing where to scaffold.

Empty folder

If it is empty, or contains only disposable operating-system metadata such as .DS_Store, create the project directly in the current folder. Remove only those disposable metadata files first, since create-video rejects non-empty folders. Do not treat all hidden files as disposable: files such as .env and directories such as .git are meaningful contents.

Scaffold in existing folder:

bash
npx create-video@latest --yes --blank --no-tailwind .
npm i

Non-empty folder

If the current folder contains meaningful contents and no project already exists, scaffold into a new subfolder. Replace my-video with a suitable project name.

bash
npx create-video@latest --yes --blank --no-tailwind my-video
cd my-video
npm i

Open the preview before building the video

As soon as the project can run, open Remotion Studio in the browser before writing or changing the composition. For a new project, do this immediately after scaffolding and installing dependencies. For an existing project, do it before editing the video. Keep Studio running while you work so the user can see changes as they appear and steer the result.

Designing a video

Keep the scaffold and add React Markup. Follow Remotion React Markup Best Practices and Video Layout Rules for video-first layout and text sizing guidance.

Before writing the root that registers <Composition> or <Still> elements, follow Compositions and stills.

Is this a timeline of clips?

If the video arranges multiple video or audio clips on a timeline, follow Video editing before writing the timeline. Give every clip that should be edited independently its own authored JSX node.

Is this a multi-scene video?

If this is a video with multiple subsequent scenes, follow guidance at Multi-scene videos.

Interactivity Best Practices

By structuring the React Markup following Remotion Interactivity Best Practices, you allow the user to make edits in the Studio which write back to code.

TailwindCSS

If Tailwind is requested, see tailwind.md for using TailwindCSS in Remotion.

Follow-up

The video creation process has finished. For follow-up prompts, use Remotion Best Practices


Subgroup 2: React Video Markup, Transitions & Audio (/remotion-markup)

This is guidance for writing Remotion React Markup. If this is not relevant, load Remotion Best Practices instead.

Preserve user changes

Users may make edits in the code outside of the conversation.

If you detect a surprising change made in the meanwhile, don't overwrite it, assume it was intentional or ask for confirmation.

General rules

Inside a composition or scene component, get fps from useVideoConfig() for all seconds-to-frames calculations, including timing props such as from, trimBefore and durationInFrames. Keeping the expression on the JSX node, such as trimBefore={4 * fps}, lets Studio edit the timing and keeps it correct if the composition frame rate changes.

Drive animations using useCurrentFrame() and interpolate(). CSS transition or animation will not render correctly, they need to refactored. Tailwind animation class will not render correctly, they need to be refactored.

Use Easing.bezier() and Easing.spring() to customize timing.

Use Track for successive scenes. Make scenes and reusable elements with Interactive.create(), setting layout explicitly to 'absolute-fill' or 'none', and register them as connected compositions. Put timing directly on each instance. Follow the complete Track + connected compositions example. See Remotion Interactivity for editable props and keyframes.

The Studio edits the JSX source node that created an item. Author every composition registration, clip, scene, layer and sequence that should be editable independently as its own JSX node, with its editable props inline. Programmatic loops are suitable when the generated instances are intentionally controlled as one source template, not when users need to edit the instances separately.

tsx
import { useCurrentFrame, useVideoConfig, Easing, interpolate, Interactive } from "remotion";

export const FadeIn = () => { const frame = useCurrentFrame(); const {fps} = useVideoConfig();

return ( <Interactive.Div name="Title" style={{ opacity: interpolate(frame, [0, 2 * fps], [0, 1], { extrapolateRight: "clamp", extrapolateLeft: "clamp", easing: Easing.bezier(0.16, 1, 0.3, 1), }), }} > Hello World! </Interactive.Div> ); }; ```

Keep the interpolate() call inline in the style prop. Use scale, translate, rotate CSS properties over transform.

tsx
// 👍 Inline editable keyframes and transform shorthands
style={{
  scale: interpolate(frame, [0, 100], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
    output: 'perceptual-scale' // For `scale` animations, use "output: 'perceptual-scale'"
  }),
  translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
  rotate: interpolate(frame, [0, 100], ["20deg", "90deg"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
}}

// 👎 Non-inline values and transform strings become harder to edit in Studio const scale = interpolate(frame, [0, 100], [0, 1]);

style={{ transform: scale(${scale}), }} ```

Assets

Place assets in the public/ folder at your project root. Use staticFile() to reference files from the public/ folder.

Media components

Add video and audio using <Video> and <Audio> from @remotion/media. Add images using the <CanvasImage> component. Add animated GIFs, APNG, WebP or AVIF images using <AnimatedImage>, use @remotion/gif if not using Chrome. Use staticFile() for files in public/ or pass a remote URL directly:

tsx
import { Audio, Video } from "@remotion/media";
import { staticFile, CanvasImage, AnimatedImage } from "remotion";

export const MyComposition = () => { return ( <> <Video src={staticFile("video.mp4")} style={{ opacity: 0.5 }} /> <Audio src={staticFile("audio.mp3")} /> <CanvasImage src={staticFile("logo.png")} style={{ width: 100, height: 100 }} /> <Video src="https://remotion.media/video.mp4" /> <AnimatedImage src={staticFile('nyancat.gif')} /> </> ); }; ```

If the composition is primarily a timeline of video or audio clips, read video-editing.md before choosing its source structure.

Example scene

A background video with a lower third. The lower third is an interactive component with its own timeline, registered as a connected composition. Its text is passed as children and accentColor is an editable prop. The fade-in is keyframed inline at the call site.

tsx
// MyScene.tsx
import { Video } from "@remotion/media";
import { Easing, Interactive, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
import { LowerThird } from "./LowerThird";

const MySceneInner = () => { const { fps } = useVideoConfig(); const frame = useCurrentFrame();

return ( <> <Video name="Background" src="https://remotion.media/video.mp4" objectFit="cover" style={{ position: "absolute", width: "100%", height: "100%", }} /> <LowerThird name="Lower third" from={1 * fps} accentColor="#0b84f3" style={{ opacity: interpolate(frame, [1 * fps, 2 * fps], [0, 1], { extrapolateRight: "clamp", extrapolateLeft: "clamp", easing: Easing.bezier(0.16, 1, 0.3, 1), }), }} > Jane Doe, Product Designer </LowerThird> </> ); };

export const MyScene = Interactive.create({ Component: MySceneInner, layout: "absolute-fill", }); ```

tsx
// LowerThird.tsx
import type React from "react";
import { Interactive, type InteractivitySchema } from "remotion";

type LowerThirdProps = { readonly children: React.ReactNode; readonly accentColor: string; readonly style?: React.CSSProperties; };

const LowerThirdInner: React.FC<LowerThirdProps> = ({ children, accentColor, style, }) => { return ( <div style={{ position: "absolute", left: 80, bottom: 80, display: "flex", alignItems: "center", gap: 20, backgroundColor: "white", borderRadius: 16, padding: "20px 32px", color: "black", fontFamily: "Helvetica, Arial, sans-serif", fontSize: 48, fontWeight: 600, ...style, }} > <div style={{ width: 8, alignSelf: "stretch", borderRadius: 4, backgroundColor: accentColor, }} /> {children} </div> ); };

const lowerThirdSchema = { ...Interactive.childrenSchema, ...Interactive.textSchema, accentColor: { type: "color", default: "#0b84f3", description: "Accent color", }, } as const satisfies InteractivitySchema;

export const LowerThird = Interactive.create({ Component: LowerThirdInner, layout: "none", schema: lowerThirdSchema, }); ```

tsx title="src/Root.tsx"
import { Composition } from "remotion";
import { LowerThird } from "./LowerThird";
import { MyScene } from "./MyScene";

export const RemotionRoot: React.FC = () => { return ( <> <Composition id="MyScene" component={MyScene} durationInFrames={60} fps={30} width={1280} height={720} /> <Composition id="LowerThird" component={LowerThird} durationInFrames={30} fps={30} width={1280} height={720} defaultProps={{ children: "Jane Doe, Product Designer", accentColor: "#0b84f3", }} /> </> ); }; ```

Delaying, trimming

Put timing directly on components that support it. See Timing props for the supported props, examples, and order of operations.

Maps

See Remotion Maps if wanting to include maps in the video.

Text highlights and annotations

See text-highlights.md for text highlights (highlight markers), circles, underlines, strike-throughs, crossed-off text, boxes.

Multi-scene videos

See multi-scene-video.md if planning to make a video with multiple subsequent scenes.

Connected compositions

When a scene or group of layers deserves its own editable timeline, follow connected-compositions.md. Prefer this structure for substantial scenes in a multi-scene video.

Pre-compose action

For a Studio request such as Pre-compose Ambient glow (src/BarChart.tsx:134), find the selected sequence markup at the given location. Make a connected composition, following connected-compositions.md: extract the markup into a named component, preferably make it interactive with Interactive.create(), and register the same exported component reference with a unique <Composition> in the root. Render the interactive component directly with its timing props, or as the only child of a sequence when that wrapper has a purpose. If the selected node is already a sequence, keep its props and extract its children. The registration needs dimensions, fps, duration, and defaultProps equivalent to its parent use. A component extraction without a registered composition does not complete a pre-compose request.

Carry inherited styles such as fontFamily and font loading into the extracted component; see parent independence.

Voiceover

See voiceover.md for adding an AI-generated voiceover to Remotion compositions using ElevenLabs TTS.

Embedding Videos

See embedding-videos.md for advanced knowledge about embedding videos - trimming, volume, speed, looping, pitch.

Embedding Audio

See audio.md for advanced audio features like trimming, volume, speed, pitch.

Cropping

See cropping.md if needing to crop the visible rectangle of a component.

Transitions

See transitions.md for scene transition patterns.

Motion blur

When adding motion blur or a movement trail, read motion-blur.md for the preferred HTML-in-canvas approach, preview requirements, and alternatives.

Visual and pixel effects

When adding a visual effect, first identify the element or group it should affect. If the target component supports effects and a built-in effect below matches the request, prefer that effect over a CSS approximation. For example, apply vignette() directly to a <Video>'s effects prop when the vignette should affect that video.

These components accept effects directly:

Import fromComponents
remotion<Img>, <CanvasImage>, <AnimatedImage>, <Solid>, <HtmlInCanvas>
@remotion/media<Video>
@remotion/gif<Gif>
@remotion/rive<RemotionRiveCanvas>
@remotion/shapes<Arrow>, <Callout>, <Circle>, <Ellipse>, <Heart>, <Pie>, <Polygon>, <Rect>, <Spark>, <Star>, <Triangle>

Ordinary HTML elements, <Interactive.*> DOM wrappers, <AbsoluteFill>, and <Sequence> do not accept effects directly. To apply effects to their content, wrap them in <HtmlInCanvas> and put the effects prop on the wrapper.

For other elements, prefer regular HTML and CSS where suitable. Consider wrapping a group in `<HtmlInCanvas>` when post-processing the whole group is appropriate and the environment supports it.

Read effects.md before applying an effect for imports, parameters, and setup. It also covers reusable custom effects with createEffect() when no built-in effect matches.

Available effects

Built-in effects from @remotion/effects:

  • Color and tone: brightness(), colorCorrection(), colorKey(), contrast(), duotone(), exposure(), grayscale(), hue(), invert(), levels(), linearGradient(), linearGradientTint(), lut(), saturation(), shadowsHighlights(), thermalVision(), tint(), vibrance(), whiteBalance().
  • Blur and trails: blur(), lightTrail(), linearProgressiveBlur(), radialProgressiveBlur(), regionBlur(), zoomBlur().
  • Lighting: dropShadow(), glow(), lightLeak(), shine(), starburst(), vignette().
  • Distortion and transforms: barrelDistortion(), chromaticAberration(), cornerPin(), fisheye(), mirror(), noiseDisplacement(), scale(), skew(), tile(), uvTranslate(), wave(), xyTranslate().
  • Textures and patterns: burlap(), checkerboard(), contourLines(), dotGrid(), emboss(), flannel(), gridlines(), halftone(), halftoneLinearGradient(), lines(), liquidContours(), noise(), paper(), pattern(), rings(), scanlines(), speckle(), tvSignalOff(), waves(), whiteNoise(), zigzag().
  • Edges: outline(), roughenEdges(), shrinkwrap(), tear().
  • Pixelation and transitions: evolve(), linearProgressivePixelate(), pixelate(), pixelDissolve(), radialProgressivePixelate(), venetianBlinds().

Color correction

Read color-correction.md for how to apply color correction, either using the colorCorrection() effect or lut() to apply a 3D .cube color grade.

3D content

See ./3d.md for 3D content in Remotion using Three.js and React Three Fiber.

Sound effects

When needing to use sound effects, load the ./sfx.md file for more information.

Audio visualization

When needing to visualize audio (spectrum bars, waveforms, bass-reactive effects), load the ./audio-visualization.md file for more information.

Maps

For static maps, animated routes and markers, geographic explainers, Mapbox, MapLibre, MapTiler, GeoJSON, or 3D geographic flyovers, load Remotion Maps.

Captions

When dealing with captions or subtitles, load the Remotion Captions skill for more information.

Google Fonts

Is the recommended way to load fonts in Remotion. See google-fonts.md for how to load Google Fonts.

Local fonts

See local-fonts.md for how to load local fonts.

GIFs

See gifs.md for how to display GIFs synchronized with Remotion's timeline.

Advanced Images

See images.md for sizing and positioning images, dynamic image paths, and getting image dimensions.

Lottie animations

See lottie.md for embedding Lottie animations in Remotion.

Timing

See timing.md for more timing techniques for interpolate().

Parameterized videos

See parameters.md for making a composition parametrizable by adding a Zod schema.

Measuring DOM nodes

See measuring-dom-nodes.md for measuring DOM element dimensions in Remotion.

Measuring text

See measuring-text.md for measuring text dimensions, fitting text to containers, and checking overflow.

Using FFmpeg

For some video operations, such as trimming videos or detecting silence, FFmpeg should be used. Load the ./ffmpeg.md file for more information.

Silence detection

When needing to detect and trim silent segments from video or audio files, load the ./silence-detection.md file.

Dynamic duration, dimensions and data

See calculate-metadata.md for dynamically set composition duration, dimensions, and props.

Compositions and stills

Before registering <Composition> or <Still> elements, read compositions.md for source-editable registrations, folders, default props and nesting. For Studio navigation into a scene's own timeline, use connected compositions.

Advanced sequencing

See sequencing.md for more sequencing patterns - delay, trim, limit duration of items.

Install modules

Use npx remotion add to add new packages with the right version:

npx remotion add @remotion/media

This goes for @remotion/* packages, mediabunny, @mediabunny/*, zod, and @huggingface/transformers.

Visual checks

When a visual check is useful, open the Remotion Studio for an interactive preview.

You can also use Rendering to inspect one or several frames as images.


Subgroup 3: Studio Live Preview & Interactivity (/remotion-studio)

When creating or editing a video, start Studio and open its preview as soon as the project can run, before building the composition. Keep it open while you work so the user can see changes and steer.

In Cursor, run Studio without --no-open so the browser opens automatically:

bash
npx remotion studio

In other agent clients, you can also let Studio open the browser automatically with the command above. If you have an in-app browser and intend to use it, run:

bash
npx remotion studio --no-open

If the Studio is already opened, the URL will be printed and the command will exit. Otherwise, a long-running process will start, and the URL will be printed.

When using --no-open, open the exact printed URL in the in-app browser and verify that Studio loads. Once a composition exists, verify that its video preview loads. Do not use --no-open if you cannot open the URL in the in-app browser.

Useful flags

ArgumentPurpose
--log=<level>Set error, warn, info (default), or verbose logging.
--port=<number>Request a Studio server port; otherwise Remotion finds a free port.
--force-newStart another Studio instance even when one is already running for the same project and port.

Interactive Studio Elements

By writing Remotion markup in a specific way, the Remotion Studio is able to recognize the structure of the code and makes it interactive:

  • Allowing items to be selected by clicking on them
  • Allowing drag+drop, resizing and rotation
  • Editing the CSS styles
  • Making keyframes and easing values editable

If the markup is too complex for the Studio to make it interactive, then the values become grayed out.

Prefer interactive components with their own timelines

Build new compositions with Track, connected compositions, and Interactive.create().

Set layout explicitly to 'absolute-fill' for scenes or 'none' for bounded elements. Register the same exported scene and reusable element components with Composition.

Start with the complete Track + connected compositions example. It includes the element, scene, parent timeline, and root registrations.

tsx title="MyVideo.tsx"
import {Track, useVideoConfig} from 'remotion';
import {Chapter} from './Chapter';

export const MyVideo = () => { const {fps} = useVideoConfig();

return ( <Track name="Chapters"> <Chapter name="Opening" durationInFrames={3 * fps} title="Introduction" /> <Chapter name="Feature" from={3 * fps} durationInFrames={4 * fps} title="New features" /> </Track> ); }; ```

Put background and alignment styles on each scene instance, as shown in the complete example. Keep layout-only containers as plain HTML.

Choose schema fields

Each key in the schema is a prop that Studio can read and edit at the call site. Props that are not in the schema still work, but are not editable in Studio. Keys may use dot notation, such as style.color.

TypeUse forKeyframable
stringStrings, such as a title, prompt, or code snippetNo
numberNumbers, with optional min, max, step, integerYes
booleanOn/off switchesYes (hold)
colorCSS color stringsYes
enumA choice between variants, each with its own nested schemaOpt-in (hold)
arrayLists of numbers, colors, enums and moreNo
assetMedia sources, with optional assetTypeNo
font-familyCSS font familyNo
font-weightFont weightYes
translate, scale, rotation-css, rotation-degrees, transform-originTransformsYes
uv-coordinateA normalized [x, y] point on the elementYes
svg-pathSVG path dataYes
remotion-captionsCaption[] dataNo
hiddenA value kept out of the controls—

Every field needs a default and should have a description, which Studio shows as the label. Set keyframable: false to show a static control.

Expose content and per-instance appearance, such as the text and an accent color. Do not add fields for style.translate, style.scale, style.rotate, style.transformOrigin and style.opacity: Interactive.create() adds them automatically.

To make CSS properties of the root editable per instance, spread the built-in schema fragments:

tsx
const cardSchema = {
  ...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
  ...Interactive.backgroundSchema, // style.backgroundColor
  ...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;

Also available: Interactive.borderSchema, and Interactive.captionsSchema for components that accept captions. For components that accept React.ReactNode children, add ...Interactive.childrenSchema. It provides controls for children content and may evolve to support richer content. Currently, it uses a string field labeled "Text" with a default of ''. If a component requires string-only children, declare an explicit children field with type: 'string' instead. Interactive.textSchema controls typography styles; childrenSchema controls content. The children fragment is not included in baseSchema or automatically added to custom components. Nested markup and computed children are currently read-only. text-content is a deprecated alias for the string field type. See `InteractivitySchema` for all options.

Register reusable components as connected compositions

Register scenes and reusable elements as standalone compositions. Studio calls these connected compositions: they can be opened in their own timeline while sharing the same component implementation with the parent video.

Define required styles and load fonts inside the component so it works without its parent. See parent independence.

Register the same exported component reference that the parent renders. For the example above, use component={Chapter}, not ChapterInner or an inline wrapper. Interactive.create() provides the sequence, so no extra <Sequence> is needed for the connection.

tsx title="src/Root.tsx"
import {Composition} from 'remotion';
import {Chapter} from './Chapter';

export const RemotionRoot = () => ( <Composition id="Chapter" component={Chapter} width={1280} height={720} fps={30} durationInFrames={90} defaultProps={{ title: 'A new perspective', style: { backgroundColor: '#111111', justifyContent: 'center', alignItems: 'center', }, }} /> ); ```

Choose a unique ID, representative inline defaults, and dimensions, fps, and duration suitable for previewing the component. Registration defaults do not automatically copy props from a parent instance. See connected compositions for registration and extraction details.

Put timing directly on the component

Avoid a separate <Sequence> around one component that already accepts timing props. Built-in interactive components and custom components made with Interactive.create() can receive name, from, durationInFrames, trimBefore, and playbackRate directly.

tsx
<Chapter
  name="Opening scene"
  from={30}
  durationInFrames={90}
  style={{opacity: 0.9}}
  title="A new perspective"
/>

For shared scene timing, group the content in a component made with Interactive.create() and time that instance. Managed roots also accept width and height for useVideoConfig().

Use <Track> to put items on the same Studio timeline row. Use <TransitionSeries> when scenes are sequential.

Give every independently editable item its own JSX node

The Studio edits the JSX source node that created an item. If multiple runtime items come from the same JSX node, they share one source-editing target.

For every composition registration, clip, scene, layer or sequence that should be editable on its own, write a separate JSX node and keep its editable props on that node. This applies to <Composition>, <Still>, built-in media components, <Sequence>, <TransitionSeries.Sequence> and custom components.

For example, author an editable timeline like this:

tsx title="Separate source nodes"
import { Track } from "remotion";

<Track name="Scenes"> <Introduction name="Introduction" durationInFrames={90} /> <Demo name="Demo" from={90} durationInFrames={150} /> </Track> ```

A .map() or another programmatic loop would create multiple runtime items from one JSX source node. That is appropriate for repeated output that is intentionally controlled as one template, such as visualization bars or particles. It is not appropriate when the instances need independent IDs or names, props, metadata, timing, ordering, deletion or duplication in the Studio.

Make an HTML element interactive using `Interactive`

Every HTML and SVG element (except <Img>, it already is interactive) such as <div> can be turned interactive using Interactive:

tsx title="Interactive elements"
<Interactive.Div
  name="Greeting card"
  style={{fontSize: 80, padding: 24}}
>
  Hello
</Interactive.Div>

This allows styles and keyframes to be set in the Studio. Be sensible, if a component has many elements, the timeline might get messy.

Prefer inline text

If text is fixed and only used once, write it directly inside the interactive element instead of extracting it into a constant.

tsx title="Inline text"
// 👍 Fixed copy stays editable
<Interactive.Div name="Title">
  Remotion Best Practices
</Interactive.Div>

Use a prop or variable only when the text is dynamic or reused.

Give interactive elements a descriptive name

Add a name prop to elements to make them easily identifyable. Avoid computed names, hardcode them.

tsx title="Interactive names"
<>
  <Interactive.Div name="Hero title" style={{fontSize: 80}}>
    Launch day
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Background" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="Title">
    Launch day
  </Interactive.Div>
</>

Keep all CSS styles inline

The best way is to just pass a plain object to style - no referring to constants, no object spreading, no math.

tsx title="Interactive example"
<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Hello World!
</Interactive.Div>
tsx title="❌ Bad for interactivity"
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // ❌ Non-inline styles are not supported
  }
}, []);

<Interactive.Div style={{ ...baseStyle, // ❌ Spreading is not supported color: RED, // ❌ Referring to constants is not supported scale: frame * 10 // ❌ Math is not supported }} > Hello World! </Interactive.Div> ```

Animate using `interpolate()`

Write animations as inline interpolate() calls on the property that changes. The output range, easing, extrapolation and output property should use hardcoded values.

The input range may additionally use durationInFrames, fps, width and height destructured directly from useVideoConfig(). Bare identifiers such as durationInFrames, multiplication with a number such as 2 * fps or fps * 2, and subtraction of a number such as durationInFrames - 1 are supported.

tsx title="Inline values"
const {fps, durationInFrames} = useVideoConfig();

// 👍 Inline values can be standardized and keyframed <Interactive.Div name="Product card" style={{ color: 'white', fontSize: 80, scale: interpolate(frame, [0, fps], [0, 1], { easing: Easing.spring({damping: 200}), output: 'perceptual-scale', extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }), rotate: interpolate(frame, [0, 1 * fps], ['0deg', '20deg'], { easing: Easing.spring({damping: 200}), extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }), translate: interpolate( frame, [durationInFrames - 30, durationInFrames], ['0px 0px', '0px 120px'], { easing: Easing.spring({damping: 200}), output: 'perceptual-scale', extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ), }} /> ```

tsx title="❌ Bad interactivity"
const translateY = interpolate(frame, [0, 30], [0, 120]); // ❌ Math should be directly in the markup

<Interactive.Div name="Product card" style={{ translate: translateY, // ❌ Only inline interpolate() calls are supported, rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ Cannot use math with arbitrary variables, cannot use constants scale: interpolate(anyVariable, [0, 30], [0, 1]) // ❌ Can only interpret the frame variable. }} /> ```

Keep SVG paths editable with `Interactive.Path`

Use <Interactive.Path> from remotion instead of <path> inside an SVG to make the path visually editable.

Install @remotion/paths for path interpolation and Studio path keyframes:

sh
bunx remotion add @remotion/paths

If the geometry is meant to be static, put the path string directly in the d prop. Do not extract it into a constant.

tsx title="Editable static path"
import {Interactive} from 'remotion';

<Interactive.Svg width={300} height={300} viewBox="0 0 300 300"> <Interactive.Path name="Triangle" d="M 40 40 L 260 40 L 150 260 Z" fill="#0b84f3" /> </Interactive.Svg> ```

Morph paths using inline `interpolatePaths()`

Use interpolatePaths() from @remotion/paths directly in d. It accepts a frame, an input range, an equally sized array of path strings, and options for easing, extrapolation, and posterization. Keep the output paths, ranges, and options inline, following the same input-range rules as interpolate() above. Do not use interpolatePath() API or extract the interpolated result into a variable when the path keyframes should remain editable in Studio.

tsx title="Editable path keyframes"
import {interpolatePaths} from '@remotion/paths';
import {Easing, Interactive, useCurrentFrame} from 'remotion';

export const MorphingPath = () => { const frame = useCurrentFrame();

return ( <Interactive.Svg width={300} height={300} viewBox="0 0 300 300"> <Interactive.Path name="Morphing triangle" d={interpolatePaths( frame, [0, 30, 60], [ 'M 40 40 L 260 40 L 150 260 Z', 'M 40 150 L 150 40 L 260 150 Z', 'M 40 260 L 150 40 L 260 260 Z', ], { easing: Easing.bezier(0.42, 0, 0.58, 1), extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }, )} fill="#0b84f3" /> </Interactive.Svg> ); }; ```

Studio can edit the path at the current frame, add or move keyframes, and adjust their easing. Use strokeDasharray and strokeDashoffset to evolve paths.

Use `scale`, `translate`, `rotate` CSS properties

Avoid the transform CSS property. If possible, use scale, rotate and translate instead because only they are interactively editable.

Keep composition metadata inline

When scaffolding a composition, use a JSX string literal for id, keep width, height, fps, durationInFrames and defaultProps inline and make no type assertions.

The Props editor can save visual edits back to your code when defaultProps is an inline object literal on <Composition> or <Still>.

tsx
// 👍 Static values are in <Composition>, dynamic values are in calculateMetadata()
const calculateMetadata = useMemo(async () => {
  const dimensions = await getDimensions(); // just an example
  return {width: dimensions.width, height: dimensions.height};
});

<Composition id="my-video" component={MyComponent} durationInFrames={150} fps={30} calculateMetadata={calculateMetadata} defaultProps={{title: 'Hello', color: '#0b84ff'}} /> ```

tsx title="Negative examples"
const defaultProps = {title: 'Hello', color: '#0b84ff'}; // ❌ Don't extract defaultProps, must be inline
const calculateMetadata = useMemo(() => {
  // ❌ Unnecessary because no calculation is being done,
  return {durationInFrames: 150, fps: 30, width: 1920, height: 1080};
});

<Composition id="my-video" component={MyComponent} calculateMetadata={calculateMetadata} defaultProps={{ title: 'Hello', } as Props} // ❌ Don't have type assertions, instead type MyComponent correctly /> ```

Use only calculateMetadata() for the part of the metadata that is dynamic.

Effects should be inline too

Keep effect arrays and parameters inline so their shape and keyframes remain editable. See Inline effects for examples.

Making your own component interactive

To make a custom userland component interactive, use: Make a component interactive

Video editing

If a Remotion component mainly consists of video and audio clips, see Video editing for best practices on how to structure Remotion markup so the clips are interactively editable in the timeline.


Subgroup 4: Programmatic Rendering & Pipelines (/remotion-render)

General rendering strategy

Render a video using:

npx remotion render

Full list of options: https://www.remotion.dev/docs/cli/render.md

Render a still using:

npx remotion still

Full list of options: https://www.remotion.dev/docs/cli/still.md

To render several frames as images in one call, use render --frames:

npx remotion render [composition-id] out/frames --frames=0,30,90 --image-format=png

See https://www.remotion.dev/docs/cli/render.md#--frames for more options.

Transparent videos

See Transparent videos for rendering out a video with transparency.


Subgroup 5: Geographic Map Animations & 3D Flyovers (/remotion-maps)

Remotion Maps

Choose exactly one technique from the intended shot, then load only that technique's TECHNIQUE.md. Every technique directory is self-contained and may be removed without breaking the others.

[Static map](techniques/static-map/TECHNIQUE.md)

  • Requires you grab a satellite image and mount it in a <Img> tag, and animate on top

[Mapbox](techniques/mapbox/TECHNIQUE.md)

  • Requires a Mapbox key
  • Nicer styles by default
  • Map can display a round globe when zoomed out
  • Includes nice 3D buildings such as the Eiffel tower

[MapLibre](techniques/maplibre/TECHNIQUE.md)

  • Requires no API key, fully free
  • Does not include 3D building

[MapTiler](techniques/maptiler/TECHNIQUE.md)

  • Uses MapTiler
  • Annotations can be drawn on top of geographic features: borders, rivers, labels

[CesiumJS](techniques/cesium/TECHNIQUE.md)

  • Flythroughs through terrain and mountains
  • "Flight simulator" perspective

Subgroup 6: Captions, Multimedia & SaaS Architecture (/remotion-saas)

Captions & Subtitles

All captions must be processed in JSON. The captions must use the `Caption` type which is the following:

ts
import type { Caption } from "@remotion/captions";

This is the definition:

ts
type Caption = {
  text: string;
  startMs: number;
  endMs: number;
  timestampMs: number | null;
  confidence: number | null;
  pageBreakAfter?: boolean;
};

Generating captions

To transcribe video and audio files to generate captions, load the transcribe-captions.md file for more instructions.

Displaying captions

To display captions in your video, load the display-captions.md file for more instructions.

Importing captions

To import captions from a .srt file, load the import-srt-captions.md file for more instructions.

Post-creation

After you're done, load this skill: Remotion Best Practices

If not alreay done, open the Remotion Studio, unless instructed otherwise.


SaaS Architecture & Player Embedding

One can build apps with Remotion. It is possible to have a simple form and hook it up to a render, or have a complex video editor.

Choosing a template or a framework

We have several templates for SaaS which can be cloned or used as a reference. See Choosing a framework for help choosing a template or framework.

The `<Player>`

This component allows embedding a Remotion preview in a React app. See Player for more information about the Player.

Rendering

There are client-side and server-side rendering options available. See Rendering for advice on how to choose, and about the Lambda, Vercel, Node.js and Cloudflare options.

With Vue

See https://www.remotion.dev/docs/vue.md for how to use Remotion with Vue.

Angular

See https://www.remotion.dev/docs/angular.md for how to use Remotion with Angular.

Svelte

See https://www.remotion.dev/docs/svelte.md for how to use Remotion with Svelte.


In-Browser Multimedia Handling (Mediabunny)

Mediabunny is a multimedia library for dealing with audio and video in the browser. Here is a compact overview of its capabilities: https://mediabunny.dev/llms.txt

Getting audio duration

See get-audio-duration.md for getting the duration of an audio file in seconds with Mediabunny.

Getting video dimensions

See get-video-dimensions.md for getting the width and height of a video file with Mediabunny.

Getting video duration

See get-video-duration.md for getting the duration of a video file in seconds with Mediabunny.