Rendering Optimization
To ensure smooth playback in the Remotion Studio and high-quality exports, your video components must be optimized for frame-by-frame rendering. Unlike standard web applications, Remotion requires deterministic rendering where every frame is a pure function of the frame number.
Driving Animations via useCurrentFrame
The most critical optimization is ensuring that all visual changes are driven by the useCurrentFrame() hook. Using standard CSS transitions or animations will result in "flickering" because the browser's animation engine runs independently of Remotion’s frame sequencer.
The Deterministic Rule
Forbidden:
- CSS
@keyframesortransition setIntervalorsetTimeoutMath.random()orDate.now()(without seeding)
Required:
Use the interpolate and spring functions provided by Remotion to map the current frame to a visual value.
import { useCurrentFrame, useVideoConfig, interpolate } from 'remotion';
export const OptimizedComponent = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Calculate values derived strictly from the frame
const opacity = interpolate(frame, [0, 20], [0, 1], {
extrapolateRight: 'clamp',
});
return <div style={{ opacity }}>Deterministic Animation</div>;
};
Memoizing Heavy Calculations
Because Remotion executes your component function for every single frame during the render process (e.g., 1,800 times for a 60-second video at 30fps), heavy calculations can significantly slow down your build time.
Using useMemo for Derived State
Use useMemo to cache complex data transformations, SVG path calculations, or expensive string manipulations that do not change every frame.
import { useMemo } from 'react';
import { useCurrentFrame } from 'remotion';
const MyVideo = ({ data }) => {
const frame = useCurrentFrame();
// Only re-calculate if the raw data props change
const processedData = useMemo(() => {
return data.map(item => complexCalculation(item));
}, [data]);
// Use the pre-processed data to drive the frame-specific UI
return <div>{processedData[frame % processedData.length]}</div>;
};
Avoiding Hydration Flicker
Flickering often occurs when a component attempts to calculate dimensions or state using browser APIs that aren't available during the initial render or are inconsistent between the Studio and the renderer.
Preventing Layout Shifts
- Use Remotion Components: Always use
<Img>,<Video>, and<Audio>. These components are designed to "block" the frame from rendering until the underlying asset is fully loaded, preventing white flashes. - Measure DOM Nodes Correctly: If you must measure a DOM element, use the
continueRenderanddelayRenderAPIs to pause the engine until measurements are complete. - Static Width/Height: Avoid
width: "100%"where possible. UseuseVideoConfig()to get exact pixel dimensions for layouts.
3D Rendering Optimization
When using @remotion/three, traditional React Three Fiber (R3F) patterns must be modified to prevent dropped frames and inconsistencies.
- No
useFrame: The R3FuseFramehook runs on the browser's requestAnimationFrame, which is non-deterministic. It is forbidden in Remotion. - Manual Rotation/Motion: Pass the value from
useCurrentFrame()directly into therotationorpositionprops of your meshes.
import { useCurrentFrame } from 'remotion';
const Box = () => {
const frame = useCurrentFrame();
// Rotate exactly 0.05 radians per frame
return <mesh rotation={[0, frame * 0.05, 0]} />;
};
Using calculateMetadata for Heavy Setup
If your composition depends on external data or media dimensions, don't fetch this inside the component body. Use calculateMetadata to resolve this data once before the render loop begins.
// In Root.tsx
<Composition
id="DynamicComp"
component={MyComp}
calculateMetadata={async ({ props }) => {
const data = await fetch(props.apiUrl).then(res => res.json());
return {
props: { ...props, fetchedData: data },
durationInFrames: data.length * 30,
};
}}
/>
This ensures that the component itself remains a synchronous, pure function during the frame-by-frame export process.