Skip to content

Core Concepts

Frame-Driven Rendering

A Rendiv video is a pure function of the current frame number. Every frame, your React component re-renders with a new frame value. By varying your output based on this number, you create animation.

tsx
import { useFrame } from '@rendiv/core';

export const MyScene = () => {
  const frame = useFrame(); // 0, 1, 2, 3, ...
  return <div style={{ opacity: frame / 30 }}>Fading in</div>;
};

There is no imperative animation API. No timelines to manage. Just React re-rendering at each frame.

Compositions

A <Composition> registers a video with its metadata. It renders nothing to the DOM — it's purely a registration mechanism.

tsx
import { setRootComponent, Composition } from '@rendiv/core';

setRootComponent(() => (
  <Composition
    id="MyVideo"
    component={MyScene}
    durationInFrames={150}  // 5 seconds at 30fps
    fps={30}
    width={1920}
    height={1080}
  />
));

You can register multiple compositions in a single entry point. The Studio, Player, and Renderer each read this registry.

Sequences

<Sequence> controls when a component appears in the timeline.

tsx
import { Sequence } from '@rendiv/core';

export const MyVideo = () => (
  <>
    <Sequence from={0} durationInFrames={60}>
      <TitleCard />  {/* Frames 0-59 */}
    </Sequence>
    <Sequence from={60} durationInFrames={90}>
      <MainContent />  {/* Frames 60-149 */}
    </Sequence>
  </>
);

Children of a <Sequence> see their own local frame starting at 0. Inside <MainContent>, useFrame() returns 0 at the global frame 60.

Series

<Series> chains sequences automatically without manual offset math.

tsx
import { Series } from '@rendiv/core';

export const MyVideo = () => (
  <Series>
    <Series.Sequence durationInFrames={60}>
      <Intro />
    </Series.Sequence>
    <Series.Sequence durationInFrames={90}>
      <MainContent />
    </Series.Sequence>
    <Series.Sequence durationInFrames={60}>
      <Outro />
    </Series.Sequence>
  </Series>
);

Loop

<Loop> repeats its children using modulo arithmetic.

tsx
import { Loop } from '@rendiv/core';

<Loop durationInFrames={30}>
  <PulsingDot />  {/* Sees frames 0-29, repeating forever */}
</Loop>

Freeze

<Freeze> locks children at a specific frame.

tsx
import { Freeze } from '@rendiv/core';

<Freeze frame={0}>
  <MyScene />  {/* Always sees frame 0 */}
</Freeze>

Fill

<Fill> is a full-size container matching the composition dimensions.

tsx
import { Fill } from '@rendiv/core';

<Fill style={{ background: '#000', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
  <h1>Centered content</h1>
</Fill>

Folders

<Folder> groups compositions in the Studio sidebar.

tsx
import { Folder, Composition } from '@rendiv/core';

<Folder name="Demos">
  <Composition id="Demo1" ... />
  <Composition id="Demo2" ... />
</Folder>

holdRender / releaseRender

The renderer captures screenshots frame-by-frame. If an async resource (font, image, data) hasn't loaded yet, the frame would be wrong. The hold/release pattern blocks frame capture until resources are ready.

ts
import { holdRender, releaseRender } from '@rendiv/core';

const handle = holdRender('Loading data...', { timeoutInMilliseconds: 10000 });
const data = await fetchMyData();
// update state
releaseRender(handle);

Media components (<Img>, <Video>, <IFrame>, <AnimatedImage>) and font hooks (useLocalFont, useFont) call holdRender/releaseRender automatically.

Released under the Apache License 2.0.