Displaying the current time
When rendering a <Player>
in your app, special considerations must be taken to prevent constant re-renders of the app or <Player>
if the time changes.
This is why the useCurrentFrame()
hook does not work outside a composition.
Do not put this hook into the same component in which the <Player>
is rendered, otherwise you'll see constant re-renders. Instead, put it inside a component that is rendered adjacent to the component in which the Player is rendered.
Synchronizing a component with the Player time
If you want to display a component that synchronizes with the time of the player, for example the cursor of a timeline component or a custom time display, you can use the following hook:
use-current-player-frame.tstsx
import {CallbackListener ,PlayerRef } from '@remotion/player';import {useCallback ,useSyncExternalStore } from 'react';export constuseCurrentPlayerFrame = (ref :React .RefObject <PlayerRef | null>,) => {constsubscribe =useCallback ((onStoreChange : () => void) => {const {current } =ref ;if (!current ) {return () =>undefined ;}constupdater :CallbackListener <'frameupdate'> = ({detail }) => {onStoreChange ();};current .addEventListener ('frameupdate',updater );return () => {current .removeEventListener ('frameupdate',updater );};},[ref ],);constdata =useSyncExternalStore <number>(subscribe ,() =>ref .current ?.getCurrentFrame () ?? 0,() => 0,);returndata ;};
use-current-player-frame.tstsx
import {CallbackListener ,PlayerRef } from '@remotion/player';import {useCallback ,useSyncExternalStore } from 'react';export constuseCurrentPlayerFrame = (ref :React .RefObject <PlayerRef | null>,) => {constsubscribe =useCallback ((onStoreChange : () => void) => {const {current } =ref ;if (!current ) {return () =>undefined ;}constupdater :CallbackListener <'frameupdate'> = ({detail }) => {onStoreChange ();};current .addEventListener ('frameupdate',updater );return () => {current .removeEventListener ('frameupdate',updater );};},[ref ],);constdata =useSyncExternalStore <number>(subscribe ,() =>ref .current ?.getCurrentFrame () ?? 0,() => 0,);returndata ;};
Usage example
Add a ref to a React Player and pass it to another component:
tsx
import {Player ,PlayerRef } from '@remotion/player';import {useRef } from 'react';import {MyVideo } from './remotion/MyVideo';import {TimeDisplay } from './remotion/TimeDisplay';export constApp :React .FC = () => {constplayerRef =useRef <PlayerRef >(null);return (<><Player ref ={playerRef }component ={MyVideo }durationInFrames ={120}compositionWidth ={1920}compositionHeight ={1080}fps ={30}/><TimeDisplay playerRef ={playerRef } /></>);};
tsx
import {Player ,PlayerRef } from '@remotion/player';import {useRef } from 'react';import {MyVideo } from './remotion/MyVideo';import {TimeDisplay } from './remotion/TimeDisplay';export constApp :React .FC = () => {constplayerRef =useRef <PlayerRef >(null);return (<><Player ref ={playerRef }component ={MyVideo }durationInFrames ={120}compositionWidth ={1920}compositionHeight ={1080}fps ={30}/><TimeDisplay playerRef ={playerRef } /></>);};
This is how a component could access the current time:
TimeDisplay.tsxtsx
importReact from 'react';import {PlayerRef } from'@remot ion/player';import {useCurrentP ame} from './use-current-player-frame';layerFr export constTimeDisplay :ct. Rea FC <{playerRef :React .RefObject <PlayerRef | null>;}> = ({playerRef }) => {constframe =useCurrentPlayerFrame (playerRef );return <div >cur rent frame: {frame }</div >;};
TimeDisplay.tsxtsx
importReact from 'react';import {PlayerRef } from'@remot ion/player';import {useCurrentP ame} from './use-current-player-frame';layerFr export constTimeDisplay :ct. Rea FC <{playerRef :React .RefObject <PlayerRef | null>;}> = ({playerRef }) => {constframe =useCurrentPlayerFrame (playerRef );return <div >cur rent frame: {frame }</div >;};
This approach is efficient, because only the video itself and the component relying on the time are re-rendering, but the <App>
component is not.