add-remotion-layer
Add a new Remotion export layer to ProAnimate. Covers layer component creation, VideoComposition.tsx registration, and frame-based rendering.
SKILL.mdAutoAnimation/.claude/skills/add-remotion-layer/SKILL.md
GitHubAdd Remotion Export Layer to ProAnimate
Create a new Remotion layer for exporting a feature to video. Remotion layers render frame-by-frame during export and live preview.
Steps
Create the Remotion layer at
src/remotion/Remotion<Name>Layer.tsximport { useFrame } from '@/engine' interface Remotion<Name>LayerProps { items: <ItemType>[] // Data passed from VideoComposition } export function Remotion<Name>Layer({ items }: Remotion<Name>LayerProps) { const frame = useFrame() return ( <> {items.map((item) => { // Skip invisible or out-of-range items if (!item.visible) return null if (frame < item.startFrame || frame >= item.endFrame) return null const style: React.CSSProperties = { position: 'absolute', left: item.position.x, top: item.position.y, opacity: item.opacity, zIndex: item.zIndex, } if (item.rotation !== 0) { style.transform = `rotate(${item.rotation}deg)` } return ( <div key={item.id} style={style}> {/* Render the item */} </div> ) })} </> ) }Define props interface in
src/remotion/types.tsAdd the data shape to
VideoCompositionProps:export interface VideoCompositionProps { // ... existing props <name>Data?: <ItemType>[] }Register in VideoComposition at
src/remotion/VideoComposition.tsxImport the layer:
import { Remotion<Name>Layer } from './Remotion<Name>Layer'Add to the composition JSX (inside the CameraTransformWrapper):
{props.<name>Data && props.<name>Data.length > 0 && ( <Remotion<Name>Layer items={props.<name>Data} /> )}Pass data from stores where VideoComposition is instantiated
In the export pipeline, store data is read and serialized into the composition props. Find where
VideoCompositionPropsis assembled and add:<name>Data: use<Name>Store.getState().items,
Key Patterns
Frame-based rendering
const frame = useFrame() // Current frame number (from @/engine, NOT from 'remotion')
// Filter by frame range
if (frame < item.startFrame || frame >= item.endFrame) return null
// Compute progress within the item's range
const progress = (frame - item.startFrame) / (item.endFrame - item.startFrame)
Keyframe interpolation
import { useRemotionKeyframeValues } from './useRemotionKeyframes'
// Inside a component:
const kfValues = useRemotionKeyframeValues(keyframeData, 'my-type', item.id)
const x = kfValues.x ?? item.position.x
const y = kfValues.y ?? item.position.y
const opacity = kfValues.opacity ?? item.opacity
Absolute positioning
All Remotion layers use absolute positioning within the composition frame:
<div style={{
position: 'absolute',
left: item.position.x,
top: item.position.y,
width: displayWidth,
height: displayHeight,
opacity: item.opacity,
zIndex: item.zIndex,
}}>
Conditional rendering
Only render when there is data:
{props.myData && props.myData.length > 0 && (
<RemotionMyLayer items={props.myData} />
)}
Key Files
| Purpose | Path |
|---|---|
| Remotion layers directory | src/remotion/ |
| VideoComposition (all layers) | src/remotion/VideoComposition.tsx |
| Remotion types | src/remotion/types.ts |
| Frame hook | @/engine (useFrame) |
| Keyframe interpolation | src/remotion/useRemotionKeyframes.ts |
| Camera wrapper | src/remotion/CameraTransformWrapper.tsx |
Common Issues
- Wrong frame import: Use
useFrame()from@/engine, NOT from theremotionpackage directly. The engine wraps Remotion's frame with the project's playback system. - Data not available: Remotion layers receive data as props, not from stores. Store data must be serialized and passed through
VideoCompositionProps. - Layer order: zIndex on the layer's container
divcontrols stacking. Existing layers use ranges like 1-10 for backgrounds, 5-8 for characters, 6-7 for shapes, 8-10 for text overlays. - Blank export: If the layer renders nothing, check that
visibleand frame range filtering is correct. Also verify the data is actually passed to VideoComposition. - Performance: Avoid heavy computation inside the render function. Pre-compute values or use memoization. Each frame triggers a re-render.
Example
Reference: src/remotion/RemotionArtCurveLayer.tsx
- Accepts
ArtCurveComposition[]as props - Uses
useFrame()from@/engine - Filters by
visible,startFrame,endFrame - Renders with absolute positioning, opacity, zIndex, rotation transform
- Returns SVG content via
dangerouslySetInnerHTML
Reference: src/remotion/RemotionTextOverlay.tsx
- Uses keyframe interpolation via
useRemotionKeyframeValues - Computes text animation presets per frame
- Renders text with full CSS styling