Player - Best practices
Avoid re-renders of the <Player>
The following pattern is not ideal because every time the time updates, the <Player>
is being re-rendered:
❌ Problematictsx
export constApp :React .FC = () => {constplayerRef =useRef <PlayerRef >(null);const [currentTime ,setCurrentTime ] =useState (0);useEffect (() => {playerRef .current ?.addEventListener ('timeupdate', (e ) => {setCurrentTime (e .detail .frame );});}, []);return (<div ><Player ref ={playerRef }component ={MyVideo } {...otherProps } /><div >Current time: {currentTime }</div ></div >);};
❌ Problematictsx
export constApp :React .FC = () => {constplayerRef =useRef <PlayerRef >(null);const [currentTime ,setCurrentTime ] =useState (0);useEffect (() => {playerRef .current ?.addEventListener ('timeupdate', (e ) => {setCurrentTime (e .detail .frame );});}, []);return (<div ><Player ref ={playerRef }component ={MyVideo } {...otherProps } /><div >Current time: {currentTime }</div ></div >);};
We advise to render your controls and UI as a sibling to the component that renders the <Player>
and pass a ref to the player as a prop:
✅ Bettertsx
constPlayerOnly :React .FC <{playerRef :React .RefObject <PlayerRef | null>;}> = ({playerRef }) => {return <Player ref ={playerRef }component ={MyVideo } {...otherProps } />;};constControlsOnly :React .FC <{playerRef :React .RefObject <PlayerRef | null>;}> = ({playerRef }) => {const [currentTime ,setCurrentTime ] =useState (0);useEffect (() => {playerRef .current ?.addEventListener ('timeupdate', (e ) => {setCurrentTime (e .detail .frame );});}, []);return <div >Current time: {currentTime }</div >;};export constApp :React .FC = () => {constplayerRef =useRef <PlayerRef >(null);return (<><PlayerOnly playerRef ={playerRef } /><ControlsOnly playerRef ={playerRef } /></>);};
✅ Bettertsx
constPlayerOnly :React .FC <{playerRef :React .RefObject <PlayerRef | null>;}> = ({playerRef }) => {return <Player ref ={playerRef }component ={MyVideo } {...otherProps } />;};constControlsOnly :React .FC <{playerRef :React .RefObject <PlayerRef | null>;}> = ({playerRef }) => {const [currentTime ,setCurrentTime ] =useState (0);useEffect (() => {playerRef .current ?.addEventListener ('timeupdate', (e ) => {setCurrentTime (e .detail .frame );});}, []);return <div >Current time: {currentTime }</div >;};export constApp :React .FC = () => {constplayerRef =useRef <PlayerRef >(null);return (<><PlayerOnly playerRef ={playerRef } /><ControlsOnly playerRef ={playerRef } /></>);};
This is much more performant because the <Player>
gets rerendered less often.
This advice is mainly for frequently updated state like the current time. Keeping state like a loop
toggle in the parent component is fine, because it is not expected to change frequently.
A more complex example can be found here.
Pass the user interaction event to play()
When you listen to an onClick()
event, the browser will give you an event
argument.
Pass it on to .play()
and .toggle()
to have the lowest chance of hitting autoplay restrictions that the browser may impose.
Memoize the inputProps
Not memoizing the inputProps
can cause the whole tree to re-render too many times and create a bottleneck.
Player.tsxtsx
import {Player } from '@remotion/player';import {useState ,useMemo } from 'react';import {MyVideo } from './remotion/MyVideo';export constApp :React .FC = () => {const [text ,setText ] =useState ('world');constinputProps =useMemo (() => {return {text ,};}, [text ]);return (<Player component ={MyVideo }durationInFrames ={120}compositionWidth ={1920}compositionHeight ={1080}fps ={30}inputProps ={inputProps }/>);};
Player.tsxtsx
import {Player } from '@remotion/player';import {useState ,useMemo } from 'react';import {MyVideo } from './remotion/MyVideo';export constApp :React .FC = () => {const [text ,setText ] =useState ('world');constinputProps =useMemo (() => {return {text ,};}, [text ]);return (<Player component ={MyVideo }durationInFrames ={120}compositionWidth ={1920}compositionHeight ={1080}fps ={30}inputProps ={inputProps }/>);};