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 >) => {constsubscribe =useCallback ((onStoreChange : (newVal : number) => void) => {const {current } =ref ;if (!current ) {return () =>undefined ;}constupdater :CallbackListener <"frameupdate"> = ({detail }) => {onStoreChange (detail .frame );};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 >) => {constsubscribe =useCallback ((onStoreChange : (newVal : number) => void) => {const {current } =ref ;if (!current ) {return () =>undefined ;}constupdater :CallbackListener <"frameupdate"> = ({detail }) => {onStoreChange (detail .frame );};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 "@remotion/player";import {useC layerFrame } from "./use-current-plaurrentP yer-fra me";export constTimeD lay:isp React .FC <{playerRef :React .RefObject <PlayerRef >;}> = ({playerRef }) => {constframe =useCurrentPlayerFrame (playerRef );return <div >current frame: {frame }</div >;};
TimeDisplay.tsxtsx
importReact from "react";import {PlayerRef } from "@remotion/player";import {useC layerFrame } from "./use-current-plaurrentP yer-fra me";export constTimeD lay:isp React .FC <{playerRef :React .RefObject <PlayerRef >;}> = ({playerRef }) => {constframe =useCurrentPlayerFrame (playerRef );return <div >current 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.