agentsclimarketplace

Remotion best practices

Skill medy-gribkov/arcana/skills/remotion-best-practices

Video creation in React using Remotion. Covers animations, compositions, audio sync, text effects, 3D integration with Three.js, and rendering. Use when building dynamic video generators, editing timelines, or optimizing Remotion renders.From its SKILL.md

Install
npx -y skills add medy-gribkov/arcana --skill remotion-best-practices

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.5 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

You are a Remotion expert. All animations must be frame-driven via useCurrentFrame(). CSS transitions and Tailwind animation classes are FORBIDDEN in Remotion, they will not render correctly.

When to use

  • Building programmatic videos with React
  • Creating animated intros, demos, promo videos
  • Generating data-driven video content
  • Working with captions, charts, or text animations

Core Pattern

Every Remotion component follows this structure:

import { useCurrentFrame, useVideoConfig, interpolate, spring } from "remotion";

export const MyScene: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps, width, height } = useVideoConfig();

  const opacity = interpolate(frame, [0, 1.5 * fps], [0, 1], {
    extrapolateRight: "clamp",
  });

  const scale = spring({ frame, fps, config: { damping: 12 } });

  return (
    <div style={{ opacity, transform: `scale(${scale})` }}>
      Content here
    </div>
  );
};

Always use seconds * fps, never raw frame numbers:

// BAD
const opacity = interpolate(frame, [0, 45], [0, 1]);

// GOOD
const opacity = interpolate(frame, [0, 1.5 * fps], [0, 1]);

Composition Setup

// src/Root.tsx
import { Composition } from "remotion";
import { MyVideo, MyVideoProps } from "./MyVideo";

export const RemotionRoot = () => (
  <Composition
    id="MyVideo"
    component={MyVideo}
    durationInFrames={30 * 30} // 30s at 30fps
    fps={30}
    width={1920}
    height={1080}
    defaultProps={{ title: "Demo" } satisfies MyVideoProps}
  />
);

Use type for props (not interface) to ensure defaultProps type safety.

Sequencing Scenes

import { Sequence } from "remotion";

export const MyVideo: React.FC = () => (
  <>
    <Sequence from={0} durationInFrames={150}>
      <Intro />
    </Sequence>
    <Sequence from={150} durationInFrames={240}>
      <MainContent />
    </Sequence>
    <Sequence from={390} durationInFrames={150}>
      <Outro />
    </Sequence>
  </>
);

Inside each <Sequence>, useCurrentFrame() resets to 0. Design components to start at frame 0.

Animation Patterns

Spring (natural motion, 0 to 1):

const scale = spring({ frame, fps, config: { mass: 1, damping: 10, stiffness: 100 } });

Interpolate (map frame range to value range):

import { interpolate, Easing } from "remotion";

const translateY = interpolate(frame, [0, 2 * fps], [50, 0], {
  extrapolateRight: "clamp",
  easing: Easing.out(Easing.cubic),
});

Staggered items:

{items.map((item, i) => {
  const delay = i * 5; // 5 frame stagger
  const progress = spring({ frame: frame - delay, fps });
  return (
    <div key={i} style={{ opacity: progress, transform: `translateY(${(1 - progress) * 20}px)` }}>
      {item}
    </div>
  );
})}

Audio

import { Audio, interpolate } from "remotion";
import { staticFile } from "remotion";

<Audio
  src={staticFile("music.mp3")}
  startFrom={30}           // trim start (frames)
  endAt={300}              // trim end (frames)
  volume={(f) =>
    interpolate(f, [0, 30], [0, 1], { extrapolateRight: "clamp" })
  }
/>

Assets

Always use staticFile() for local assets in public/:

// BAD
<Img src="/logo.png" />

// GOOD
import { staticFile } from "remotion";
<Img src={staticFile("logo.png")} />

For Google Fonts:

import { loadFont } from "@remotion/google-fonts/Inter";
const { fontFamily } = loadFont();

Anti-patterns

BAD:

// CSS transition (won't render in video)
<div style={{ transition: "opacity 0.3s" }}>

// Tailwind animate class (won't render)
<div className="animate-bounce">

// Raw frame numbers (unreadable)
const x = interpolate(frame, [0, 90], [0, 100]);

// useEffect for animation (breaks determinism)
useEffect(() => { setOpacity(1); }, []);

GOOD:

// Frame-driven opacity
const opacity = interpolate(frame, [0, 3 * fps], [0, 1], {
  extrapolateRight: "clamp",
});

// Spring for natural motion
const scale = spring({ frame, fps });

// All state derived from frame, never useState for animation

Rendering

# Preview
npx remotion preview

# Render to MP4
npx remotion render MyVideo out/video.mp4

# Render specific frames
npx remotion render MyVideo --frames=0-150 out/preview.mp4

# Render as GIF
npx remotion render MyVideo out/video.gif --image-format=png

Deep Dive References

For detailed patterns, load the relevant rule file:

TopicFile
Compositions, stills, foldersrules/compositions.md
Interpolation and springsrules/timing.md
Scene transitionsrules/transitions.md
Text animationsrules/text-animations.md
Captions and subtitlesrules/subtitles.md
Charts and data vizrules/charts.md
3D with Three.jsrules/3d.md
Video embeddingrules/videos.md
Audio and soundrules/audio.md
Parametrizable videos (Zod)rules/parameters.md
TailwindCSS setuprules/tailwind.md
Maps (Mapbox)rules/maps.md
Transparent video exportrules/transparent-videos.md

What ships with it: 36 files

97.7 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.