Skip to main content

Render videos programmatically from a dataset

You can use Remotion to do a batch render to create many videos based on a dataset. In the following example, we are going to turn a JSON dataset into a series of videos.

We'll start by creating a blank Remotion project:

npm init video --blank

Sample dataset​

JSON is the most convenient format to import in Remotion. If your dataset is in a different format, you can convert it using one of many available libraries on NPM.

my-data.ts
export const data = [ { name: 'React', repo: 'facebook/react', logo: 'https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg', }, { name: 'Remotion', repo: 'remotion-dev/remotion', logo: 'https://github.com/remotion-dev/logo/raw/main/withouttitle/element-0.png', }, ];

Sample component​

This component animates a title, subtitle and image using Remotion. Create src/MyScene.tsx with the following:

MyScene.tsx
import React from 'react'; import {Easing, Img, Interactive, interpolate, useCurrentFrame} from 'remotion'; type Props = { name: string; logo: string; repo: string; }; export const MyScene: React.FC<Props> = ({name, repo, logo}) => { const frame = useCurrentFrame(); return ( <Interactive.Div name="Repository card" style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', scale: interpolate(frame, [10, 40], [0, 1], { easing: Easing.spring({damping: 100}), extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), backgroundColor: 'white', fontWeight: 'bold', justifyContent: 'center', alignItems: 'center', }} > <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 20, }} > <Img src={logo} style={{ height: 80, }} /> <div style={{ display: 'flex', flexDirection: 'column', }} > <Interactive.Div name="Repository name" style={{ fontSize: 40, translate: interpolate(frame, [20, 30], ['0px 10px', '0px 0px'], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), lineHeight: 1, }} > {name} </Interactive.Div> <Interactive.Div name="Repository path" style={{ fontSize: 20, opacity: interpolate(frame, [30, 40], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), lineHeight: 1.25, }} > {repo} </Interactive.Div> </div> </div> </Interactive.Div> ); };

Register MyScene in src/Root.tsx:

Root.tsx
import {Composition} from 'remotion'; import {MyScene} from './MyScene'; export const RemotionRoot = () => ( <Composition id="MyComp" component={MyScene} durationInFrames={60} fps={30} width={1920} height={1080} defaultProps={{ name: 'Remotion', repo: 'remotion-dev/remotion', logo: 'https://github.com/remotion-dev/logo/raw/main/withouttitle/element-0.png', }} /> );

Writing the script​

In order to render our videos, we'll first need to bundle our project using Webpack and prepare it for rendering. This can be done by using the bundle() function from the @remotion/bundler package. Make sure to include the webpack override in the bundle if you have one.

import {bundle} from '@remotion/bundler';
import {webpackOverride} from './webpack-override';

const bundleLocation = await bundle({
  entryPoint: './src/index.ts',
  // If you have a webpack override, don't forget to add it
  webpackOverride: webpackOverride,
});

Rendering videos​

Import the dataset and loop over each entry.

Fetch the composition metadata for each render using selectComposition(). You have the opportunity to parametrize the duration, width and height of the composition based on the data entry with the calculateMetadata() function.

Trigger a render using renderMedia() and pass the data entry as inputProps. This will pass the object as React props to the component above.

import {renderMedia, selectComposition} from '@remotion/renderer';
import {data} from './dataset';

for (const entry of data) {
  const composition = await selectComposition({
    serveUrl: bundleLocation,
    id: compositionId,
    inputProps: entry,
  });

  await renderMedia({
    composition,
    serveUrl: bundleLocation,
    codec: 'h264',
    outputLocation: `out/${entry.name}.mp4`,
    inputProps: entry,
  });
}
note

It is not recommended to render more than one video at a time, because each render already tries to use as many resources as your machine gives.

Full script​

render.mjs
import {selectComposition, renderMedia} from '@remotion/renderer'; import {webpackOverride} from './webpack-override'; import {bundle} from '@remotion/bundler'; import {data} from './dataset'; const compositionId = 'MyComp'; const bundleLocation = await bundle({ entryPoint: './src/index.ts', // If you have a webpack override in remotion.config.ts, pass it here as well. webpackOverride: webpackOverride, }); for (const entry of data) { const composition = await selectComposition({ serveUrl: bundleLocation, id: compositionId, inputProps: entry, }); await renderMedia({ composition, serveUrl: bundleLocation, codec: 'h264', outputLocation: `out/${entry.name}.mp4`, inputProps: entry, }); }

Running the script​

node render.mjs

To use TypeScript, rename the file to render.ts, install ts-node from npm and run ts-node render.ts. If you get errors, wrap the asynchronous code in an async function and call it.

Rendering videos from a CSV dataset​

Use a package like csv2json to convert your dataset into a JSON.

See also​