/remotion
Best practices and domain knowledge for building videos programmatically with Remotion (videos in React/TypeScript, rendered to MP4). Use whenever writing or editing Remotion code — scaffolding a video project, animating with interpolate/spring over useCurrentFrame(), sequencing
$ npx -y skills add guanyang/open-agent-hub --skill remotion --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/remotion
Context preview
The summary Claude sees to decide when to auto-load this skill.
Best practices and domain knowledge for building videos programmatically with Remotion (videos in React/TypeScript, rendered to MP4). Use whenever writing or editing Remotion code — scaffolding a video project, animating with interpolate/spring over useCurrentFrame(), sequencing
SKILL.md
remotion.SKILL.mdname: remotion-best-practices
description: Best practices and domain knowledge for building videos programmatically with Remotion (videos in React/TypeScript, rendered to MP4). Use whenever writing or editing Remotion code — scaffolding a video project, animating with interpolate/spring over useCurrentFrame(), sequencing scenes and transitions, captions/subtitles, audio, GIFs/Lottie/3D, charts, visual effects, dynamic duration via calculateMetadata, or rendering. Consult before reaching for a Remotion API, since its components and props evolve (e.g. @remotion/media).
metadata:
tags: remotion, video, react, animation, composition
When to use
Use this skill whenever you are dealing with Remotion code to obtain the domain-specific knowledge.
New project setup
When in an empty folder or workspace with no existing Remotion project, scaffold one using:
npx create-video@latest --yes --blank --no-tailwind my-video
Replace `my-video` with a suitable project name.
Designing a video
Before designing visual scenes, layouts, promos, motion graphics, or text-heavy videos, load [rules/video-layout.md](rules/video-layout.md) for video-first layout and text sizing guidance.
Animate properties using `useCurrentFrame()` and `interpolate()`. Prefer `interpolate()` over `spring()` unless physics-based motion is explicitly needed. Use `Easing.bezier()` to customize timing, including jumpy or overshooting motion.
For animations that should be editable in Remotion Studio, keep the `interpolate()` call inline in the `style` prop and use individual CSS transform properties (`scale`, `translate`, `rotate`) instead of composing a `transform` string.
import { useCurrentFrame, Easing, interpolate, useVideoConfig } from "remotion";
export const FadeIn = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const opacity = interpolate(frame, [0, 2 * fps], [0, 1], {
extrapolateRight: "clamp",
extrapolateLeft: "clamp",
easing: Easing.bezier(0.16, 1, 0.3, 1),
});
return <div style={{ opacity }}>Hello World!</div>;
};Prefer:
style={{
scale: interpolate(frame, [0, 100], [0, 1]),
translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"]),
rotate: interpolate(frame, [0, 100], ["20deg", "90deg"]),
}}Over:
const scale = interpolate(frame, [0, 100], [0, 1]);
style={{
transform: `scale(${scale})`,
}}CSS transitions or animations are FORBIDDEN - they will not render correctly. Tailwind animation class names are FORBIDDEN - they will not render correctly.
Place assets in the `public/` folder at your project root.
Use `staticFile()` to reference files from the `public/` folder.
Add images using the `<Img>` component:
import { Img, staticFile } from "remotion";
export const MyComposition = () => {
return <Img src={staticFile("logo.png")} style={{ width: 100, height: 100 }} />;
};Add videos using the `<Video>` component from `@remotion/media`:
import { Video } from "@remotion/media";
import { staticFile } from "remotion";
export const MyComposition = () => {
return <Video src={staticFile("video.mp4")} style={{ opacity: 0.5 }} />;
};Add audio using the `<Audio>` component from `@remotion/media`:
import { Audio } from "@remotion/media";
import { staticFile } from "remotion";
export const MyComposition = () => {
return <Audio src={staticFile("audio.mp3")} />;
};Assets can be also referenced as remote URLs:
import { Video } from "@remotion/media";
export const MyComposition = () => {
return <Video src="https://remotion.media/video.mp4" />
};To delay content wrap it in `<Sequence>` and use `from`. To limit the duration of an element, use `durationInFrames` of `<Sequence>`. `<Sequence>` by default is an absolute fill. For inline content, use `layout="none"`.
import { Sequence } from "remotion";
export const Title = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const opacity = interpolate(frame, [0, 2 * fps], [0, 1], {
extrapolateRight: "clamp",
extrapolateLeft: "clamp",
easing: Easing.bezier(0.16, 1, 0.3, 1),
});
return <div style={{ opacity }}>Title</div>;
};
export const Subtitle = () => {
return <div>Subtitle</div>;
};
const Main = () => {
const {fps} = useVideoConfig();
return (
<AbsoluteFill>
<Sequence>
<Background />
</Sequence>
<Sequence from={1 * fps} durationInFrames={2 * fps} layout="none">
<Title />
</Sequence>
<Sequence from={2 * fps} durationInFrames={2 * fps} layout="none">
<Subtitle />
</Sequence>
</AbsoluteFill>
);
}The width, height, fps, and duration of a video is defined in `src/Root.tsx`:
import { Composition } from "remotion";
import { MyComposition } from "./MyComposition";
export const RemotionRoot = () => {
return (
<Composition
id="MyComposition"
component={MyComposition}
durationInFrames={100}
fps={30}
width={1080}
height={1080}
/>
);
};Metadata can also be calculated dynamically:
import { Composition, CalculateMetadataFunction } from "remotion";
import { MyComposition, MyCompositionProps } from "./MyComposition";
const calculateMetadata: CalculateMetadataFunction<
MyCompositionProps
> = async ({ props, abortSignal }) => {
const data = await fetch(`https://api.example.com/video/${props.videoId}`, {
signal: abortSignal,
}).then((res) => res.json());
return {
durationInFrames: Math.ceil(data.duration * 30),
props: {
...props,
videoUrl: data.url,
},
width: 1080,
height: 1080,
};
};
export const RemotionRoot = () => {
return (
<Composition
id="MyComposition"
component={MyComposition}
fps={30}
width={1080}
height={1080}
defaultProps={{ videoId: "abc123" }}
calculateMetadata={calculateMetadata}Read more
name: remotion-best-practices description: Best practices and domain knowledge for building videos programmatically with Remotion (videos in React/TypeScript, rendered to MP4). Use whenever writing or editing Remotion code — scaffolding a video project, animating with interpolate/spring over useCurrentFrame(), sequencing scenes and transitions, captions/subtitles, audio, GIFs/Lottie/3D, charts, visual effects, dynamic duration via calculateMetadata, or rendering. Consult before reaching for a Remotion API, since its components and props evolve (e.g. @remotion/media). metadata: tags: remotion, video, react, animation, composition
When to use
Use this skill whenever you are dealing with Remotion code to obtain the domain-specific knowledge.
New project setup
When in an empty folder or workspace with no existing Remotion project, scaffold one using:
npx create-video@latest --yes --blank --no-tailwind my-video
Replace `my-video` with a suitable project name.
Designing a video
Before designing visual scenes, layouts, promos, motion graphics, or text-heavy videos, load [rules/video-layout.md](rules/video-layout.md) for video-first layout and text sizing guidance.
Animate properties using `useCurrentFrame()` and `interpolate()`. Prefer `interpolate()` over `spring()` unless physics-based motion is explicitly needed. Use `Easing.bezier()` to customize timing, including jumpy or overshooting motion.
For animations that should be editable in Remotion Studio, keep the `interpolate()` call inline in the `style` prop and use individual CSS transform properties (`scale`, `translate`, `rotate`) instead of composing a `transform` string.
import { useCurrentFrame, Easing, interpolate, useVideoConfig } from "remotion";
export const FadeIn = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const opacity = interpolate(frame, [0, 2 * fps], [0, 1], {
extrapolateRight: "clamp",
extrapolateLeft: "clamp",
easing: Easing.bezier(0.16, 1, 0.3, 1),
});
return <div style={{ opacity }}>Hello World!</div>;
};Prefer:
style={{
scale: interpolate(frame, [0, 100], [0, 1]),
translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"]),
rotate: interpolate(frame, [0, 100], ["20deg", "90deg"]),
}}Over:
const scale = interpolate(frame, [0, 100], [0, 1]);
style={{
transform: `scale(${scale})`,
}}CSS transitions or animations are FORBIDDEN - they will not render correctly. Tailwind animation class names are FORBIDDEN - they will not render correctly.
Place assets in the `public/` folder at your project root.
Use `staticFile()` to reference files from the `public/` folder.
Add images using the `<Img>` component:
import { Img, staticFile } from "remotion";
export const MyComposition = () => {
return <Img src={staticFile("logo.png")} style={{ width: 100, height: 100 }} />;
};Add videos using the `<Video>` component from `@remotion/media`:
import { Video } from "@remotion/media";
import { staticFile } from "remotion";
export const MyComposition = () => {
return <Video src={staticFile("video.mp4")} style={{ opacity: 0.5 }} />;
};Add audio using the `<Audio>` component from `@remotion/media`:
import { Audio } from "@remotion/media";
import { staticFile } from "remotion";
export const MyComposition = () => {
return <Audio src={staticFile("audio.mp3")} />;
};Assets can be also referenced as remote URLs:
import { Video } from "@remotion/media";
export const MyComposition = () => {
return <Video src="https://remotion.media/video.mp4" />
};To delay content wrap it in `<Sequence>` and use `from`. To limit the duration of an element, use `durationInFrames` of `<Sequence>`. `<Sequence>` by default is an absolute fill. For inline content, use `layout="none"`.
import { Sequence } from "remotion";
export const Title = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const opacity = interpolate(frame, [0, 2 * fps], [0, 1], {
extrapolateRight: "clamp",
extrapolateLeft: "clamp",
easing: Easing.bezier(0.16, 1, 0.3, 1),
});
return <div style={{ opacity }}>Title</div>;
};
export const Subtitle = () => {
return <div>Subtitle</div>;
};
const Main = () => {
const {fps} = useVideoConfig();
return (
<AbsoluteFill>
<Sequence>
<Background />
</Sequence>
<Sequence from={1 * fps} durationInFrames={2 * fps} layout="none">
<Title />
</Sequence>
<Sequence from={2 * fps} durationInFrames={2 * fps} layout="none">
<Subtitle />
</Sequence>
</AbsoluteFill>
);
}The width, height, fps, and duration of a video is defined in `src/Root.tsx`:
import { Composition } from "remotion";
import { MyComposition } from "./MyComposition";
export const RemotionRoot = () => {
return (
<Composition
id="MyComposition"
component={MyComposition}
durationInFrames={100}
fps={30}
width={1080}
height={1080}
/>
);
};Metadata can also be calculated dynamically:
import { Composition, CalculateMetadataFunction } from "remotion";
import { MyComposition, MyCompositionProps } from "./MyComposition";
const calculateMetadata: CalculateMetadataFunction<
MyCompositionProps
> = async ({ props, abortSignal }) => {
const data = await fetch(`https://api.example.com/video/${props.videoId}`, {
signal: abortSignal,
}).then((res) => res.json());
return {
durationInFrames: Math.ceil(data.duration * 30),
props: {
...props,
videoUrl: data.url,
},
width: 1080,
height: 1080,
};
};
export const RemotionRoot = () => {
return (
<Composition
id="MyComposition"
component={MyComposition}
fps={30}
width={1080}
height={1080}
defaultProps={{ videoId: "abc123" }}
calculateMetadata={calculateMetadata}A lightweight, zero-dependency CLI tool to manage and activate capabilities for AI coding assistants (such as Claude Code, Cursor, Trae, etc.).
Repo: guanyang/open-agent-hub
Other skills on open-agent-hub.
- /advanced-evaluation
This skill should be used for advanced LLM evaluation: LLM-as-judge systems, direct scoring, pairwise comparison, rubric calibration, evaluator bias mitigation, confidence scoring, and automated quality assessment.
Open skill - /algorithmic-art
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing
Open skill - /baoyu-article-illustrator
Analyzes article structure, identifies positions requiring visual aids, generates illustrations with Type × Style × Palette three-dimension approach. Use when user asks to "illustrate article", "add images", "generate images for article", or "为文章配图".
Open skill - /baoyu-comic
Knowledge comic creator supporting multiple art styles and tones. Creates original educational comics with detailed panel layouts and batch-capable image generation. Use when user asks to create "知识漫画", "教育漫画", "biography comic", "tutorial comic", or "Logicomix-style comic".
Open skill - /baoyu-compress-image
Compresses images to WebP (default) or PNG with automatic tool selection. Use when user asks to "compress image", "optimize image", "convert to webp", or reduce image file size.
Open skill - /baoyu-cover-image
Generates article cover images with 5 dimensions (type, palette, rendering, text, mood) combining 11 color palettes and 7 rendering styles. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article
Open skill

