
Remotion
- 779 installs
- 1.8k repo stars
- Updated July 6, 2026
- digitalsamba/claude-code-video-toolkit
remotion is a Claude Code video-toolkit skill that teaches developers to build programmatic animated videos with React hooks like useCurrentFrame, useVideoConfig, and interpolate.
About
remotion is a skill from digitalsamba/claude-code-video-toolkit that documents the Remotion React API for generating high-quality animated videos programmatically inside Claude Code or Cursor. Core hooks include `useCurrentFrame()` for the current 0-indexed frame—relative inside `<Sequence>`—and `useVideoConfig()` returning width, height, fps, durationInFrames, id, and defaultProps. The `interpolate()` helper maps input ranges to output ranges with extrapolation and easing options, enabling motion without manual keyframe math. Developers reach for remotion when agents need to scaffold React-based video compositions, explainer animations, or data-driven motion graphics instead of manual timeline editors. The skill is reference documentation for Remotion patterns rather than a render farm or hosting service.
- useCurrentFrame() and useVideoConfig() hooks for frame-accurate control
- interpolate() with 4 extrapolation modes, easing functions, and multi-keyframe support
- spring() physics animation with 8 configurable parameters and preset configs
- Full Remotion API reference optimized for agentic code generation
- TypeScript-first video composition patterns for AI-assisted video workflows
Remotion by the numbers
- 779 all-time installs (skills.sh)
- +20 installs in the week ending Jul 24, 2026 (Skillselion tracking)
- Ranked #316 of 1,340 Generative Media skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/digitalsamba/claude-code-video-toolkit --skill remotionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 779 |
|---|---|
| repo stars | ★ 1.8k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 6, 2026 |
| Repository | digitalsamba/claude-code-video-toolkit ↗ |
How do you build programmatic React videos with Remotion?
Generate high-quality animated videos programmatically using React and the Remotion library inside Claude Code or Cursor.
Who is it for?
Frontend developers generating motion graphics or explainer videos as React code through AI coding agents.
Skip if: Teams needing non-React video editors, live-action production workflows, or server-side render pipelines without Remotion.
When should I use this skill?
An agent session needs Remotion React components, frame-based animation, or programmatic video composition code.
What you get
React Remotion components using frame hooks, Sequence timing, and interpolate-based motion curves.
- Remotion React video components
- interpolate-based animation code
- Sequence-timed frame layouts
Files
Remotion — Toolkit Extensions
Core Remotion knowledge lives in .claude/skills/remotion-official/ (synced from the official remotion-dev/skills repo). This file covers toolkit-specific patterns only.Shared Components
Reusable video components in lib/components/. Import in templates via:
import { AnimatedBackground, SlideTransition, Label } from '../../../../lib/components';| Component | Purpose |
|---|---|
AnimatedBackground | Floating shapes background (variants: subtle, tech, warm, dark) |
SlideTransition | Scene transitions (fade, zoom, slide-up, blur-fade) |
Label | Floating label badge with optional JIRA reference |
Vignette | Cinematic edge darkening overlay |
LogoWatermark | Corner logo branding |
SplitScreen | Side-by-side video comparison |
NarratorPiP | Picture-in-picture presenter overlay |
Envelope | 3D envelope with opening flap animation |
PointingHand | Animated hand emoji with slide-in and pulse |
MazeDecoration | Animated isometric grid decoration for corners |
Custom Transitions
The toolkit includes a transitions library at lib/transitions/ for scene-to-scene effects beyond the official @remotion/transitions package.
Using TransitionSeries
import { TransitionSeries, linearTiming } from '@remotion/transitions';
// Import custom transitions from lib (adjust path based on your project location)
import { glitch, lightLeak, clockWipe, checkerboard } from '../../../../lib/transitions';
// Or import from @remotion/transitions for official ones
import { slide, fade } from '@remotion/transitions/slide';
<TransitionSeries>
<TransitionSeries.Sequence durationInFrames={90}>
<TitleSlide />
</TransitionSeries.Sequence>
<TransitionSeries.Transition
presentation={glitch({ intensity: 0.8 })}
timing={linearTiming({ durationInFrames: 30 })}
/>
<TransitionSeries.Sequence durationInFrames={120}>
<ContentSlide />
</TransitionSeries.Sequence>
</TransitionSeries>Available Custom Transitions
| Transition | Options | Best For |
|---|---|---|
glitch() | intensity, slices, rgbShift | Tech demos, edgy reveals, cyberpunk |
rgbSplit() | direction, displacement | Modern tech, energetic transitions |
zoomBlur() | direction, blurAmount | CTAs, high-energy moments, impact |
lightLeak() | temperature, direction | Celebrations, film aesthetic, warm moments |
clockWipe() | startAngle, direction, segments | Time-related content, playful reveals |
pixelate() | maxBlockSize, gridSize, scanlines, glitchArtifacts, randomness | Retro/gaming, digital transformations |
checkerboard() | gridSize, pattern, stagger, squareAnimation | Playful reveals, structured transitions |
Checkerboard patterns: sequential, random, diagonal, alternating, spiral, rows, columns, center-out, corners-in
Transition Examples
// Tech/cyberpunk feel
glitch({ intensity: 0.8, slices: 8, rgbShift: true })
// Warm celebration
lightLeak({ temperature: 'warm', direction: 'right' })
// High energy zoom
zoomBlur({ direction: 'in', blurAmount: 20 })
// Chromatic aberration
rgbSplit({ direction: 'diagonal', displacement: 30 })
// Clock sweep reveal
clockWipe({ direction: 'clockwise', startAngle: 0 })
// Retro pixelation
pixelate({ maxBlockSize: 50, glitchArtifacts: true })
// Checkerboard patterns
checkerboard({ pattern: 'diagonal', gridSize: 8 })
checkerboard({ pattern: 'spiral', gridSize: 10 })
checkerboard({ pattern: 'center-out', squareAnimation: 'scale' })Transition Duration Guidelines
| Type | Frames | Notes |
|---|---|---|
| Quick cut | 15-20 | Fast, punchy |
| Standard | 30-45 | Most common |
| Dramatic | 50-60 | Slow reveals |
| Glitch effects | 20-30 | Should feel sudden |
| Light leak | 45-60 | Needs time to sweep |
Preview Transitions
Run the showcase gallery to see all transitions:
cd showcase/transitions && npm run studioToolkit Conventions
These are choices specific to this toolkit. For general Remotion best practices (interpolation clamping, useVideoConfig, delayRender, staticFile, no CSS animations), see the remotion-official skill.
1. All projects use 30fps — Timing: frames = seconds × 30 2. playbackRate must be constant — For variable/extreme speeds, pre-process with FFmpeg 3. Video/audio components — Templates use <OffthreadVideo> and <Audio> from remotion. Upstream now also documents <Video>/<Audio> from @remotion/media (newer, has trim props + pitch shifting). Both render correctly; prefer the toolkit's choice when extending existing templates for consistency.
Project Timing Conventions
| Scene Type | Duration | Notes |
|---|---|---|
| Title | 3-5s (90-150f) | Logo + headline |
| Overview | 10-20s | 3-5 bullet points |
| Demo | 10-30s | Adjust playbackRate to fit |
| Stats | 8-12s | 3-4 stat cards |
| Credits | 5-10s | Quick fade |
Pacing: ~150 words/minute for voiceover. Voiceover drives timing.
Advanced API
For detailed API documentation on all hooks, components, renderer, Lambda, and Player APIs, see reference.md.
License Note
Remotion has a special license. Companies may need to obtain a license for commercial use. Check https://remotion.dev/license
---
Feedback & Contributions
If this skill is missing information or could be improved:
- Missing a pattern? Describe what you needed
- Found an error? Let me know what's wrong
- Want to contribute? I can help you:
1. Update this skill with improvements 2. Create a PR to github.com/digitalsamba/claude-code-video-toolkit
Just say "improve this skill" and I'll guide you through updating .claude/skills/remotion/SKILL.md.
Remotion API Reference
Core Hooks
useCurrentFrame()
const frame = useCurrentFrame();Returns current frame (0-indexed). Inside <Sequence>, returns relative frame.
useVideoConfig()
const { width, height, fps, durationInFrames, id, defaultProps } = useVideoConfig();interpolate()
interpolate(
input: number,
inputRange: number[],
outputRange: number[],
options?: {
extrapolateLeft?: 'extend' | 'clamp' | 'identity' | 'wrap',
extrapolateRight?: 'extend' | 'clamp' | 'identity' | 'wrap',
easing?: (t: number) => number
}
): numberExamples:
// Basic interpolation
interpolate(15, [0, 30], [0, 100]); // 50
// With clamping
interpolate(50, [0, 30], [0, 1], { extrapolateRight: 'clamp' }); // 1
// Multiple keyframes
interpolate(frame, [0, 20, 40, 60], [0, 1, 1, 0]);
// With easing
interpolate(frame, [0, 30], [0, 100], { easing: Easing.bezier(0.42, 0, 0.58, 1) });spring()
spring({
frame: number,
fps: number,
config?: {
damping?: number, // Default: 10
mass?: number, // Default: 1
stiffness?: number, // Default: 100
overshootClamping?: boolean
},
from?: number, // Default: 0
to?: number, // Default: 1
durationInFrames?: number,
durationRestThreshold?: number,
delay?: number,
reverse?: boolean
}): numberConfig presets:
- High bounce:
{ damping: 5, stiffness: 200 } - No bounce:
{ damping: 20, stiffness: 100, overshootClamping: true } - Slow:
{ damping: 20, mass: 2 }
measureSpring()
Get the duration of a spring animation:
import { measureSpring } from 'remotion';
const duration = measureSpring({ fps: 30, config: { damping: 10 } });
// Returns number of frames until spring settlesinterpolateColors()
interpolateColors(
input: number,
inputRange: number[],
outputRange: string[], // Hex, rgb(), rgba(), hsl()
options?: { extrapolateLeft?, extrapolateRight? }
): stringEasing
import { Easing } from 'remotion';
// Basic
Easing.linear
Easing.ease
Easing.quad
Easing.cubic
// In/Out/InOut variants
Easing.in(Easing.quad)
Easing.out(Easing.cubic)
Easing.inOut(Easing.ease)
// Cubic bezier
Easing.bezier(x1, y1, x2, y2)
// Other
Easing.circle
Easing.back(s?) // Overshoot
Easing.elastic(bounciness?)
Easing.bounce
Easing.sin
Easing.exp
Easing.poly(n) // Power of nComponents
Composition
<Composition
id="MyVideo"
component={MyComponent}
// OR lazyComponent={() => import('./MyComponent')}
durationInFrames={150}
fps={30}
width={1920}
height={1080}
defaultProps={{ title: 'Hello' }}
calculateMetadata={async ({ props }) => ({
durationInFrames: props.items.length * 30,
props: { ...props, computed: true }
})}
/>Sequence
<Sequence
from={30} // Start frame
durationInFrames={60} // Optional duration
name="Intro" // Label in Studio timeline
layout="none" // "none" | "absolute-fill"
>
<Child />
</Sequence>Series
<Series>
<Series.Sequence durationInFrames={30} offset={-5}>
<A /> {/* Frames 0-29 */}
</Series.Sequence>
<Series.Sequence durationInFrames={60}>
<B /> {/* Frames 25-84 (offset caused overlap) */}
</Series.Sequence>
</Series>Loop
<Loop
durationInFrames={30}
times={3} // Or Infinity
layout="none"
>
<Animation />
</Loop>AbsoluteFill
<AbsoluteFill style={{ backgroundColor: '#000' }}>
{/* Position: absolute, full width/height */}
</AbsoluteFill>Media Components
Img (waits for load):
<Img src={staticFile('photo.jpg')} style={{ width: '100%' }} />Video/Html5Video:
<Video
src={staticFile('clip.mp4')}
volume={0.5} // 0-1, or callback: (f) => f / 100
playbackRate={1.5}
muted={false}
loop={false}
startFrom={30} // Skip first 30 frames of source
endAt={120} // Stop at frame 120 of source
acceptableTimeShiftInSeconds={0.2}
/>OffthreadVideo (better performance):
<OffthreadVideo
src={staticFile('clip.mp4')}
volume={0.5}
transparent={false} // For videos with alpha
toneMapped={true} // HDR tone mapping
/>Audio:
<Audio
src={staticFile('music.mp3')}
volume={0.8}
startFrom={0}
endAt={300}
playbackRate={1}
/>AnimatedImage (GIF/APNG):
<AnimatedImage src={staticFile('animation.gif')} />Async Handling
import { delayRender, continueRender, cancelRender } from 'remotion';
// Block render
const handle = delayRender('Loading data...');
// Unblock when ready
continueRender(handle);
// Cancel on error
cancelRender(new Error('Failed to load'));With timeout:
const handle = delayRender('Loading...', { timeoutInMilliseconds: 30000 });Static Files & Prefetching
import { staticFile, prefetch, getStaticFiles } from 'remotion';
// Reference file in public/
const url = staticFile('video.mp4');
// Prefetch for faster playback
const { free, waitUntilDone } = prefetch(url);
await waitUntilDone();
// Later: free() to release memory
// List all static files
const files = getStaticFiles(); // ['video.mp4', 'image.png', ...]Input Props
import { getInputProps } from 'remotion';
const props = getInputProps(); // Data passed via --props CLI flagEnvironment Detection
import { getRemotionEnvironment } from 'remotion';
const env = getRemotionEnvironment();
// { isStudio: boolean, isRendering: boolean, isPlayer: boolean }random()
Deterministic random for consistent renders:
import { random } from 'remotion';
const value = random('my-seed'); // 0-1, same every render
const value2 = random('seed', 0, 100); // 0-100
const value3 = random(null); // Different each render@remotion/renderer API
import { bundle } from '@remotion/bundler';
import {
renderMedia,
renderStill,
selectComposition,
getCompositions,
renderFrames,
stitchFramesToVideo
} from '@remotion/renderer';
// Bundle project
const bundleLocation = await bundle({
entryPoint: './src/index.ts',
webpackOverride: (config) => config,
});
// Get composition
const composition = await selectComposition({
serveUrl: bundleLocation,
id: 'MyComp',
inputProps: {},
});
// Render video
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: 'h264', // h264, h265, vp8, vp9, gif, prores
outputLocation: 'out.mp4',
inputProps: {},
onProgress: ({ progress }) => console.log(`${progress * 100}%`),
imageFormat: 'jpeg', // jpeg or png
jpegQuality: 80,
scale: 1,
frameRange: [0, 59], // Optional: specific frames
muted: false,
audioBitrate: '128k',
videoBitrate: '5M',
crf: 18, // Quality (lower = better, bigger)
concurrency: 4,
});
// Render still image
await renderStill({
composition,
serveUrl: bundleLocation,
output: 'thumbnail.png',
frame: 30,
imageFormat: 'png',
});@remotion/lambda API
import {
deployFunction,
deploySite,
renderMediaOnLambda,
renderStillOnLambda,
getRenderProgress,
downloadMedia,
} from '@remotion/lambda';
// Deploy function
const { functionName } = await deployFunction({
region: 'us-east-1',
timeoutInSeconds: 120,
memorySizeInMb: 2048,
});
// Deploy site
const { serveUrl } = await deploySite({
entryPoint: './src/index.ts',
region: 'us-east-1',
siteName: 'my-video',
});
// Render
const { renderId, bucketName } = await renderMediaOnLambda({
region: 'us-east-1',
functionName,
serveUrl,
composition: 'MyComp',
codec: 'h264',
inputProps: {},
framesPerLambda: 20,
});
// Check progress
const progress = await getRenderProgress({
renderId,
bucketName,
region: 'us-east-1',
functionName,
});
// Download when done
if (progress.done) {
await downloadMedia({
bucketName,
renderId,
region: 'us-east-1',
outPath: 'video.mp4',
});
}@remotion/player API
import { Player, PlayerRef } from '@remotion/player';
const playerRef = useRef<PlayerRef>(null);
<Player
ref={playerRef}
component={MyComp}
// OR lazyComponent={() => import('./MyComp')}
durationInFrames={150}
fps={30}
compositionWidth={1920}
compositionHeight={1080}
inputProps={{}}
style={{ width: '100%' }}
controls={true}
autoPlay={false}
loop={false}
showVolumeControls={true}
allowFullscreen={true}
clickToPlay={true}
doubleClickToFullscreen={true}
spaceKeyToPlayOrPause={true}
playbackRate={1}
renderLoading={() => <div>Loading...</div>}
errorFallback={({ error }) => <div>Error: {error.message}</div>}
numberOfSharedAudioTags={5}
initiallyShowControls={3000}
renderPlayPauseButton={() => null}
moveToBeginningWhenEnded={true}
/>
// Imperative API
playerRef.current?.play();
playerRef.current?.pause();
playerRef.current?.toggle();
playerRef.current?.seekTo(30);
playerRef.current?.getCurrentFrame();
playerRef.current?.isPlaying();
playerRef.current?.getVolume();
playerRef.current?.setVolume(0.5);
playerRef.current?.isMuted();
playerRef.current?.mute();
playerRef.current?.unmute();
playerRef.current?.requestFullscreen();
playerRef.current?.exitFullscreen();
playerRef.current?.isFullscreen();
// Events
<Player
onPlay={() => {}}
onPause={() => {}}
onEnded={() => {}}
onError={(e) => {}}
onSeeked={(frame) => {}}
onTimeUpdate={({ frame }) => {}}
onFullscreenChange={(isFullscreen) => {}}
/>calculateMetadata
Dynamic composition properties:
export const calculateMetadata: CalculateMetadataFunction<Props> = async ({
props,
abortSignal,
defaultProps
}) => {
const data = await fetch('/api/data', { signal: abortSignal });
return {
durationInFrames: data.items.length * 30,
fps: 60,
width: 1920,
height: 1080,
props: { ...props, items: data.items },
};
};
<Composition
id="Dynamic"
component={MyComp}
calculateMetadata={calculateMetadata}
// Base values (can be overridden by calculateMetadata)
durationInFrames={1}
fps={30}
width={1920}
height={1080}
/>Related skills
How it compares
Pick remotion when video output must be React code agents can edit; use FFmpeg CLI skills for shell-based transcoding without React composition.
FAQ
What Remotion hooks does the remotion skill document?
The remotion skill covers `useCurrentFrame()` for the current 0-indexed frame, `useVideoConfig()` for width, height, fps, and durationInFrames, and `interpolate()` for mapping input ranges to animated output values with easing options.
How does interpolate work in Remotion?
Remotion's `interpolate()` accepts an input number, inputRange, outputRange, and optional extrapolation and easing settings. For example, `interpolate(15, [0, 30], [0, 100])` returns 50 at the midpoint between frames 0 and 30.
Is Remotion safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.