delayRender() and continueRender()
delayRender() pauses the render to let an asynchronous task such as data fetching complete.
It has no effect when you are in a preview environment (e.g. Studio or Player).
This API returns a handle. Once you have fetched data or finished the asynchronous task, you should call continueRender(handle) to let Remotion know that the render can continue.
If the asynchronous task fails and you cannot recover, call cancelRender() to cancel the render.
delayRender() can be imported from the remotion package, but preferrably, you should use the useDelayRender() hook instead, because it future-proofs your code for browser rendering.
Example
tsximport {useCallback ,useEffect ,useState } from 'react';import {useDelayRender } from 'remotion';export constMyVideo = () => {const [data ,setData ] =useState (null);const {delayRender ,continueRender ,cancelRender } =useDelayRender ();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 );}}, []);useEffect (() => {fetchData ();}, []);return <div >{data ? <div >This video has data from an API! {JSON .stringify (data )}</div > : null}</div >;};
Timeout
After calling delayRender(), you need to call continueRender() within 30 seconds or the render will fail with a timeout error. You can customize the timeout.
If continueRender() is not called within the timeout frame, the render will fail with an exception similarly to this:
A delayRender() was called but not cleared after 28000ms. See https://remotion.dev/docs/timeout for help. The delayRender was called
See the Timeout page to troubleshoot timeouts.
Adding a labelv2.6.13
If you encounter a timeout and don't know where it came from, you can add a label as a parameter:
tsxdelayRender ('Fetching data from API...');
If the call times out, the label will be referenced in the error message:
Uncaught Error: A delayRender() "Fetching data from API..." was called but not cleared after 28000ms. See https://remotion.dev/docs/timeout for help. The delayRender was called
Multiple calls
You can call delayRender() multiple times. The render will be blocked for as long as at least one blocking handle exists that has not been cleared by continueRender().
tsximport {useEffect ,useState } from 'react';import {useDelayRender } from 'remotion';constMyComp :React .FC = () => {const {delayRender ,continueRender } =useDelayRender ();const [handle1 ] =useState (() =>delayRender ());const [handle2 ] =useState (() =>delayRender ());useEffect (() => {// You need to clear all handles before the render continuescontinueRender (handle1 );continueRender (handle2 );}, []);return null;};
Encapsulation
You should put delayRender() calls inside your components rather than placing them as a top-level statement, to avoid blocking a render if a different composition is rendered.
❌ Bug - Other compositions are blockedtsximport {useEffect } from 'react';import {continueRender ,delayRender } from 'remotion';// Don't call a delayRender() call outside a component -// it will block the render if a different composition is rendered// as well as block the fetching of the list of compositions.consthandle =delayRender ();constMyComp :React .FC = () => {useEffect (() => {continueRender (handle );}, []);return null;};
Also avoid creating a new handle on every React render:
❌ Bug - New handle is create on every React re-rendertsximport {useEffect } from 'react';import {continueRender ,delayRender } from 'remotion';constMyComp :React .FC = () => {// New handle is created on every React re-renderconsthandle =delayRender ();useEffect (() => {continueRender (handle );}, []);return null;};
It's best to always use useDelayRender() in the first place since this mistake cannot happen and it enables browser rendering in the future.
✅ Best practice - useDelayRender() + useState()tsximport {useEffect ,useState } from 'react';import {useDelayRender } from 'remotion';constMyComp :React .FC = () => {const {delayRender ,continueRender } =useDelayRender ();const [handle ] =useState (() =>delayRender ());useEffect (() => {continueRender (handle );}, []);return null;};
Data fetching
Note that data fetching can also be performed in calculateMetadata(), which has 2 advantages:
- It runs only once, rather than the amount of concurrency.
- You don't need to call
continueRender()orcancelRender()manually.
Failing with an errorv4.0.374
If your code fails to do an asynchronous operation and you want to cancel the render, you can call cancelRender() with an error message. This will automatically cancel all delayRender() calls to not further delay the render.
MyComposition.tsxtsximportReact , {useEffect ,useState } from 'react';import {cancelRender ,continueRender ,delayRender } from 'remotion';export constMyComp :React .FC = () => {const [handle ] =useState (() =>delayRender ('Fetching data...'));useEffect (() => {fetch ('https://example.com').then (() => {continueRender (handle );}).catch ((err ) =>cancelRender (err ));}, []);return null;};
Retryingv4.0.140
If an operation is flaky (for example, if loading an asset from a CDN does sometimes give 5xx errors), you can pass an object with a retries value as a second argument.
If a delayRender() call is not cleared within the timeout, the whole browser tab will be closed and the frame will be retried from scratch.
Retrying a delayRender()tsxdelayRender ('Loading asset...', {retries : 1, // default: 0});
The <Img>, <Video>, <Audio>, <Html5Audio>, <Html5Video> and <IFrame> tags support a delayRenderRetries prop to control the value of retries for the delayRender() call that those components make.
Modifying the timeoutv4.0.140
In addition to the global timeout that can be set, the timeout can be modified on a per-delayRender() level.
Modifying the timeout of a delayRender()tsxdelayRender ('Loading asset...', {timeoutInMilliseconds : 7000,});
The <Img>, <Video>, <Audio>, <Html5Audio>, <Html5Video> and <IFrame> tags support a delayRenderTimeoutInMilliseconds prop to control the value of timeoutInMilliseconds for the delayRender() call that those components make.
Difference to useBufferState().delayPlayback()
useBufferState() is a different API that allows pausing playback in the Studio and in the Player.
delayRender() only has an effect when a video is being rendered.
If you are loading data, you might want to both delay the screenshotting of your component during rendering and start a buffering state during Preview, in which case you need to use both APIs together.
Using delayRender() and delayPlayback() togethertsximportReact from 'react';import {useBufferState ,useDelayRender } from 'remotion';constMyComp :React .FC = () => {constbuffer =useBufferState ();const {delayRender ,continueRender } =useDelayRender ();const [handle ] =React .useState (() =>delayRender ());React .useEffect (() => {constdelayHandle =buffer .delayPlayback ();setTimeout (() => {delayHandle .unblock ();continueRender (handle );}, 5000);return () => {delayHandle .unblock ();};}, []);return <></>;};