Data fetching
In Remotion, you may fetch data from an API to use it in your video. On this page, we document recipes and best practices.
Fetching data before the renderv4.0.0
You may use the calculateMetadata prop of the <Composition /> component to alter the props that get passed to your React component.
When to use
The data being fetched using calculateMetadata() must be JSON-serializable. That means it is useful for API responses, but not for assets in binary format.
Usage
Pass a callback function which takes the untransformed props, and return an object with the new props.
src/Root.tsxtsximport {Composition } from "remotion";typeApiResponse = {title : string;description : string;};typeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = () => null;export constRoot :React .FC = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}calculateMetadata ={async ({props }) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};}}/>);};
src/Root.tsxtsximport {Composition } from "remotion";typeApiResponse = {title : string;description : string;};typeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = () => null;export constRoot :React .FC = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}calculateMetadata ={async ({props }) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};}}/>);};
The props being passed to calculateMetadata() are the input props merged together with the default props.
In addition to props, defaultProps can also be read from the same object.
When transforming, the input and the output must be the same TypeScript type.
Consider using a nullable type for your data and throw an error inside your component to deal with the null type:
MyComp.tsxtsxtypeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
MyComp.tsxtsxtypeMyCompProps = {id : string;data :ApiResponse | null;};constMyComp :React .FC <MyCompProps > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
TypeScript typing
You may use the CalculateMetadataFunction type from remotion to type your callback function. Pass as the generic value (<>) the type of your props.
src/Root.tsxtsximport {CalculateMetadataFunction } from "remotion";export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {return {props : {...props ,data : {title : "Hello world",description : "This is a description",},},};};export constMyComp :React .FC <MyCompProps > = () => null;
src/Root.tsxtsximport {CalculateMetadataFunction } from "remotion";export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {return {props : {...props ,data : {title : "Hello world",description : "This is a description",},},};};export constMyComp :React .FC <MyCompProps > = () => null;
Colocation
Here is an example of how you could define a schema, a component and a fetcher function in the same file:
MyComp.tsxtsximport {CalculateMetadataFunction } from "remotion";import {z } from "zod";constapiResponse =z .object ({title :z .string (),description :z .string () });export constmyCompSchema =z .object ({id :z .string (),data :z .nullable (apiResponse ),});typeProps =z .infer <typeofmyCompSchema >;export constcalcMyCompMetadata :CalculateMetadataFunction <Props > = async ({props ,}) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <Props > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
MyComp.tsxtsximport {CalculateMetadataFunction } from "remotion";import {z } from "zod";constapiResponse =z .object ({title :z .string (),description :z .string () });export constmyCompSchema =z .object ({id :z .string (),data :z .nullable (apiResponse ),});typeProps =z .infer <typeofmyCompSchema >;export constcalcMyCompMetadata :CalculateMetadataFunction <Props > = async ({props ,}) => {constdata = awaitfetch (`https://example.com/api/${props .id }`);constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <Props > = ({data }) => {if (data === null) {throw newError ("Data was not fetched");}return <div >{data .title }</div >;};
src/Root.tsxtsximportReact from "react";import {Composition } from "remotion";import {MyComp ,calcMyCompMetadata ,myCompSchema } from "./MyComp";export constRoot = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}schema ={myCompSchema }calculateMetadata ={calcMyCompMetadata }/>);};
src/Root.tsxtsximportReact from "react";import {Composition } from "remotion";import {MyComp ,calcMyCompMetadata ,myCompSchema } from "./MyComp";export constRoot = () => {return (<Composition id ="MyComp"component ={MyComp }durationInFrames ={300}fps ={30}width ={1920}height ={1080}defaultProps ={{id : "1",data : null,}}schema ={myCompSchema }calculateMetadata ={calcMyCompMetadata }/>);};
By implementing this pattern, The id in the props editor can now be tweaked and Remotion will refetch the data whenever it changes.
Setting the duration based on data
You may set the durationInFrames, fps, width and height by returning those keys in the callback function:
tsximport {CalculateMetadataFunction } from "remotion";typeMyCompProps = {durationInSeconds : number;};export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {constfps = 30;constdurationInSeconds =props .durationInSeconds ;return {durationInFrames :durationInSeconds *fps ,fps ,};};
tsximport {CalculateMetadataFunction } from "remotion";typeMyCompProps = {durationInSeconds : number;};export constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = ({props }) => {constfps = 30;constdurationInSeconds =props .durationInSeconds ;return {durationInFrames :durationInSeconds *fps ,fps ,};};
Learn more about this feature in the Variable metadata page.
Aborting stale requests
The props in the props editor may rapidly change for example by typing fast.
It is a good practice to cancel requests which are stale using the abortSignal that gets passed to the calculateMetadata() function:
src/MyComp.tsxtsxexport const calculateMyCompMetadata: CalculateMetadataFunction<MyCompProps> = async ({ props, abortSignal }) => {const data = await fetch(`https://example.com/api/${props.id}`, { signal: abortSignal, });const json = await data.json();return { props: {...props,data: json,},};};export const MyComp: React.FC<MyCompProps> = () => null;
src/MyComp.tsxtsxexport const calculateMyCompMetadata: CalculateMetadataFunction<MyCompProps> = async ({ props, abortSignal }) => {const data = await fetch(`https://example.com/api/${props.id}`, { signal: abortSignal, });const json = await data.json();return { props: {...props,data: json,},};};export const MyComp: React.FC<MyCompProps> = () => null;
This abortSignal is created by Remotion using the AbortController API.
Debouncing requests
If you are making requests to an expensive API, you might want to only fire a request after the user has stopped typing for a while. You may use the following function for doing so:
src/wait-for-no-input.tstsximport {getRemotionEnvironment } from "remotion";export constwaitForNoInput = (signal :AbortSignal ,ms : number) => {// Don't wait during renderingif (getRemotionEnvironment ().isRendering ) {returnPromise .resolve ();}if (signal .aborted ) {returnPromise .reject (newError ("stale"));}returnPromise .race <void>([newPromise <void>((_ ,reject ) => {signal .addEventListener ("abort", () => {reject (newError ("stale"));});}),newPromise <void>((resolve ) => {setTimeout (() => {resolve ();},ms );}),]);};
src/wait-for-no-input.tstsximport {getRemotionEnvironment } from "remotion";export constwaitForNoInput = (signal :AbortSignal ,ms : number) => {// Don't wait during renderingif (getRemotionEnvironment ().isRendering ) {returnPromise .resolve ();}if (signal .aborted ) {returnPromise .reject (newError ("stale"));}returnPromise .race <void>([newPromise <void>((_ ,reject ) => {signal .addEventListener ("abort", () => {reject (newError ("stale"));});}),newPromise <void>((resolve ) => {setTimeout (() => {resolve ();},ms );}),]);};
src/MyComp.tsxtsxexport constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = async ({props ,abortSignal }) => {awaitwaitForNoInput (abortSignal , 750);constdata = awaitfetch (`https://example.com/api/${props .id }`, {signal :abortSignal ,});constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <MyCompProps > = () => null;
src/MyComp.tsxtsxexport constcalculateMyCompMetadata :CalculateMetadataFunction <MyCompProps > = async ({props ,abortSignal }) => {awaitwaitForNoInput (abortSignal , 750);constdata = awaitfetch (`https://example.com/api/${props .id }`, {signal :abortSignal ,});constjson = awaitdata .json ();return {props : {...props ,data :json ,},};};export constMyComp :React .FC <MyCompProps > = () => null;
Time limit
When Remotion calls the calculateMetadata() function, it wraps it in a delayRender(), which by default times out after 30 seconds.
Fetching data during the render
Using delayRender() and continueRender() you can tell Remotion to wait for asynchronous operations to finish before rendering a frame.
When to use
Use this approach to load assets which are not JSON serializable or if you are using a Remotion version lower than 4.0.
Usage
Call delayRender() as soon as possible, for example when initializing the state inside your component.
tsximport {useCallback ,useEffect ,useState } from "react";import {cancelRender ,continueRender ,delayRender } from "remotion";export constMyComp = () => {const [data ,setData ] =useState (null);const [handle ] =useState (() =>delayRender ());constfetchData =useCallback (async () => {try {constresponse = awaitfetch ("http://example.com/api");constjson = awaitresponse .json ();setData (json );continueRender (handle );} catch (err ) {cancelRender (err );}}, [handle ]);useEffect (() => {fetchData ();}, [fetchData ]);return (<div >{data ? (<div >This video has data from an API! {JSON .stringify (data )}</div >) : null}</div >);};
tsximport {useCallback ,useEffect ,useState } from "react";import {cancelRender ,continueRender ,delayRender } from "remotion";export constMyComp = () => {const [data ,setData ] =useState (null);const [handle ] =useState (() =>delayRender ());constfetchData =useCallback (async () => {try {constresponse = awaitfetch ("http://example.com/api");constjson = awaitresponse .json ();setData (json );continueRender (handle );} catch (err ) {cancelRender (err );}}, [handle ]);useEffect (() => {fetchData ();}, [fetchData ]);return (<div >{data ? (<div >This video has data from an API! {JSON .stringify (data )}</div >) : null}</div >);};
Once the data is fetched, you can call continueRender() to tell Remotion to continue rendering the video.
In case the data fetching fails, you can call cancelRender() to cancel the render without waiting for the timeout.
Time limit
You need to clear all handles created by delayRender() within 30 seconds after the page is opened. You may increase the timeout.
Prevent overfetching
During rendering, multiple headless browser tabs are opened to speed up rendering.
In Remotion Lambda, the rendering concurrency may be up to 200x.
This means that if you are fetching data inside your component, the data fetching will be performed many times.
frame as a dependency of the useEffect(), directly or indirectly, otherwise data will be fetched every frame leading to slowdown and potentially running into rate limits.