Skip to main content
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>;
  • 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>
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

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.
Offset the frame passed to spring:
spring({ frame: frame - delay, fps })
Spring automatically handles negative frames (returns 0).
When exact timing doesn’t matter:
<Series>
  {scenes.map(scene => (
    <Series.Sequence durationInFrames={scene.duration}>
      {scene.component}
    </Series.Sequence>
  ))}
</Series>

Timing Guidelines

1

Choose Stagger Interval

  • Fast (3-5 frames): Energetic, rapid reveals
  • Medium (8-12 frames): Balanced, professional
  • Slow (15-20 frames): Dramatic, deliberate
2

Set Base Delay

Give the scene time to establish before elements appear:
const BASE_DELAY = 15; // ~0.5 seconds at 30fps
3

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

Build docs developers (and LLMs) love