← Back to Home

How to Generate Remotion Compositions Programmatically

Updated January 14, 2026
remotioncomposition generationreactvideoprogrammatic

Generating Remotion Compositions Programmatically

Remotion compositions can be generated as React/TypeScript files from script data.

File Structure

Compositions are saved as .tsx files:

src/compositions/{articleId}.tsx

Composition Template

import { AbsoluteFill, Sequence } from 'remotion';
import { Slide } from '../components/Slide';
import type { Scene } from '../types';

export const Article6564191: React.FC = () => {
  const scenes: Scene[] = [
    {
      sceneNumber: 1,
      headline: "AIが変えるアニメ業界",
      narration: "人工知能の台頭により...",
      visualDescription: "Futuristic animation studio...",
      duration: 12.5,
      durationInFrames: 375,
      animationConfig: { /* ... */ },
      assets: { /* ... */ },
    },
    // ... more scenes
  ];

  let currentFrame = 0;

  return (
    <AbsoluteFill>
      {scenes.map((scene, index) => {
        const sequence = (
          <Sequence
            key={scene.sceneNumber}
            from={currentFrame}
            durationInFrames={scene.durationInFrames}
          >
            <Slide scene={scene} />
          </Sequence>
        );

        currentFrame += scene.durationInFrames - 30; // 30-frame overlap
        return sequence;
      })}
    </AbsoluteFill>
  );
};

export const article6564191Config = {
  durationInFrames: 1446,
  fps: 30,
  size: { width: 1080, height: 1920 },
};

Generator Function

import fs from 'fs';
import path from 'path';

export function generateComposition(articleId: string, script: VideoScript): void {
  const scenes = script.scenes;

  // Calculate total duration with 30-frame overlaps
  let totalFrames = 0;
  scenes.forEach((scene, index) => {
    totalFrames += scene.durationInFrames;
    if (index < scenes.length - 1) {
      totalFrames -= 30; // Subtract overlap
    }
  });

  // Generate component code
  const componentCode = `
import { AbsoluteFill, Sequence } from 'remotion';
import { Slide } from '../components/Slide';
import type { Scene } from '../types';

export const Article${articleId}: React.FC = () => {
  const scenes: Scene[] = ${JSON.stringify(scenes, null, 2)};

  let currentFrame = 0;

  return (
    <AbsoluteFill>
      {scenes.map((scene, index) => {
        const sequence = (
          <Sequence
            key={scene.sceneNumber}
            from={currentFrame}
            durationInFrames={scene.durationInFrames}
          >
            <Slide scene={scene} />
          </Sequence>
        );

        currentFrame += scene.durationInFrames - 30;
        return sequence;
      })}
    </AbsoluteFill>
  );
};

export const article${articleId}Config = {
  durationInFrames: ${totalFrames},
  fps: 30,
  size: { width: 1080, height: 1920 },
};
`;

  // Write to file
  const outputPath = path.join('src/compositions', `${articleId}.tsx`);
  fs.writeFileSync(outputPath, componentCode);
}

Scene Component (Slide)

// src/components/Slide.tsx
export const Slide: React.FC<{ scene: Scene }> = ({ scene }) => {
  const { animationConfig, assets } = scene;

  return (
    <div
      style={{
        opacity: calculateOpacity(scene), // For cross-fade
        backgroundColor: '#000',
      }}
    >
      <BackgroundAnimation
        config={animationConfig?.background}
        imageSrc={staticFile(`${assets?.imageUuid}.png`)}
      />
      <SlideTitle
        config={animationConfig?.headline}
        text={scene.headline}
      />
      <AudioPlayer src={staticFile(`${assets?.audioUuid}.wav`)} />
    </div>
  );
};

Using Static Files

Assets stored in public/ directory:

import { staticFile } from 'remotion';

// File: public/abc123def456.png
const imageSrc = staticFile('abc123def456.png');

Frame Timing

// Scene 1: Frame 0-431 (audio ends at 401, fades out 401-431)
// Scene 2: Frame 401-913 (fades in 401-431, audio ends at 883, fades out 883-913)
// Scene 3: Frame 883-1446 (fades in 883-913, ...)