Impact: HIGH - Controls when elements appear and enables complex choreography
This skill covers Remotion’s timing components (Sequence and Series) and patterns for staggered animations and complex choreography.
Sequence for Delayed Elements
Use Sequence to delay when an element appears in the timeline.
{
frame >= 30 && <Title />;
}
{
frame >= 60 && <Subtitle />;
}
Using Sequence is cleaner than manual frame checks and allows child components to use useCurrentFrame() with a local frame counter (starting from 0).
Series for Sequential Playback
Use Series when elements should play one after another without overlap.
import { Series } from "remotion";
<Series>
<Series.Sequence durationInFrames={45}>
<Intro />
</Series.Sequence>
<Series.Sequence durationInFrames={60}>
<MainContent />
</Series.Sequence>
<Series.Sequence durationInFrames={30}>
<Outro />
</Series.Sequence>
</Series>;
When to use Series vs Sequence?
- Sequence: Use when you know exact frame numbers where elements should appear
- Series: Use when you want elements to play sequentially without manually calculating frame offsets
Series automatically calculates the start time of each sequence based on previous durations.
Series with Offset for Overlap
Use negative offset for overlapping sequences:
<Series>
<Series.Sequence durationInFrames={60}>
<SceneA />
</Series.Sequence>
<Series.Sequence offset={-15} durationInFrames={60}>
{/* Starts 15 frames before SceneA ends */}
<SceneB />
</Series.Sequence>
</Series>
Negative offsets create overlaps, useful for crossfade effects or maintaining visual continuity between scenes.
Staggered Element Entrances
For staggered animations of multiple items, calculate delays:
const items = data.map((item, i) => {
const delay = i === 0 ? 0 : i === 1 ? 10 : i === 2 ? 20 : 30;
// ...
});
Using calculated delays makes the code scalable - adding more items automatically adjusts the timing.
Nested Sequences
Sequences can be nested for complex timing:
<Sequence from={0} durationInFrames={120}>
<Background />
<Sequence from={15} durationInFrames={90}>
<Title />
</Sequence>
<Sequence from={45} durationInFrames={60}>
<Subtitle />
</Sequence>
</Sequence>
How do nested Sequences work?
The inner Sequence frames are relative to the parent. In this example:
- Title appears at global frame 15 (0 + 15)
- Subtitle appears at global frame 45 (0 + 45)
This allows you to move the entire group by changing only the outer from value.
Frame References Inside Sequences
Inside a Sequence, useCurrentFrame() returns the local frame (starting from 0):
<Sequence from={60} durationInFrames={30}>
<MyComponent />
{/* Inside MyComponent, useCurrentFrame() returns 0-29, not 60-89 */}
</Sequence>
Be aware that useCurrentFrame() is zero-based within each Sequence. If you need the global frame, use useVideoConfig() or pass the frame as a prop.
Key Patterns
Calculated Stagger Delays
Always use a formula for stagger delays:const delay = BASE_DELAY + index * STAGGER_DELAY;
Makes code maintainable and scalable.
Separate initial delay from stagger interval:const BASE_DELAY = 15; // All items wait this long
const STAGGER_DELAY = 8; // Additional delay per item
Provides fine-grained control over timing. Spring with Delayed Start
Offset the frame passed to spring:spring({ frame: frame - delay, fps })
Spring automatically handles negative frames (returns 0). Series for Auto-Sequencing
When exact timing doesn’t matter:<Series>
{scenes.map(scene => (
<Series.Sequence durationInFrames={scene.duration}>
{scene.component}
</Series.Sequence>
))}
</Series>
Timing Guidelines
Choose Stagger Interval
- Fast (3-5 frames): Energetic, rapid reveals
- Medium (8-12 frames): Balanced, professional
- Slow (15-20 frames): Dramatic, deliberate
Set Base Delay
Give the scene time to establish before elements appear:const BASE_DELAY = 15; // ~0.5 seconds at 30fps
Calculate Total Duration
const totalDuration = BASE_DELAY +
(itemCount * STAGGER_DELAY) +
ANIMATION_DURATION;
Common Mistakes to Avoid
Don’t use manual frame checks - Use Sequence component instead of {frame >= X && <Component />}
Don’t hardcode stagger delays - Use calculated delays: delay = i * STAGGER_DELAY
Don’t forget durationInFrames - Every Sequence needs a duration, or it will only show for 1 frame.
Don’t assume global frames inside Sequences - useCurrentFrame() is local to the Sequence, starting from 0.
Advanced: Dynamic Sequence Generation
const scenes = [
{ component: <Intro />, duration: 60 },
{ component: <MainContent />, duration: 120 },
{ component: <Outro />, duration: 45 },
];
let currentFrame = 0;
<>
{scenes.map((scene, i) => {
const sequenceStart = currentFrame;
currentFrame += scene.duration;
return (
<Sequence key={i} from={sequenceStart} durationInFrames={scene.duration}>
{scene.component}
</Sequence>
);
})}
</>
This pattern is useful when scene durations are dynamic or loaded from data.
Spring Physics
Animate staggered elements
Transitions
Transition between sequences
Charts
Stagger bar chart entrances