Displaying captions
This guide explains how to display captions in Remotion, assuming you already have captions in the Caption format - see Transcribing audio for how to generate them.
Fetching captions
First, fetch your captions JSON file. Use useDelayRender() to hold the render until the captions are loaded:
Fetching captionstsximport {useState ,useEffect ,useCallback } from 'react';import {AbsoluteFill ,staticFile ,useDelayRender } from 'remotion';import type {Caption } from '@remotion/captions';export constMyComponent :React .FC = () => {const [captions ,setCaptions ] =useState <Caption [] | null>(null);const {delayRender ,continueRender ,cancelRender } =useDelayRender ();const [handle ] =useState (() =>delayRender ());constfetchCaptions =useCallback (async () => {try {constresponse = awaitfetch (staticFile ('captions.json'));constdata = awaitresponse .json ();setCaptions (data );continueRender (handle );} catch (e ) {cancelRender (e );}}, [continueRender ,cancelRender ,handle ]);useEffect (() => {fetchCaptions ();}, [fetchCaptions ]);if (!captions ) {return null;}return <AbsoluteFill >{/* Render captions here */}</AbsoluteFill >;};
Creating pages
Use createTikTokStyleCaptions() to group captions into pages. The combineTokensWithinMilliseconds option controls how many words appear at once:
Creating caption pagestsxconst {pages } =useMemo (() => {returncreateTikTokStyleCaptions ({captions ,combineTokensWithinMilliseconds :SWITCH_CAPTIONS_EVERY_MS ,});}, [captions ]);
Rendering with Sequences
Map over the pages and render each one in a <Sequence>. Calculate the start frame and duration from the page timing:
Rendering caption pagestsxconstCaptionedContent :React .FC = () => {const {fps } =useVideoConfig ();return (<AbsoluteFill >{pages .map ((page ,index ) => {constnextPage =pages [index + 1] ?? null;conststartFrame = (page .startMs / 1000) *fps ;constendFrame =Math .min (nextPage ? (nextPage .startMs / 1000) *fps :Infinity ,startFrame + (SWITCH_CAPTIONS_EVERY_MS / 1000) *fps );constdurationInFrames =endFrame -startFrame ;if (durationInFrames <= 0) {return null;}return (<Sequence key ={index }from ={startFrame }durationInFrames ={durationInFrames }><CaptionPage page ={page } /></Sequence >);})}</AbsoluteFill >);};
Rendering a caption page
A caption page contains tokens which you can use to highlight the currently spoken word. Here's an example that highlights words as they are spoken:
Rendering a caption page with word highlightingtsximport {AbsoluteFill ,useCurrentFrame ,useVideoConfig } from 'remotion';import type {TikTokPage } from '@remotion/captions';constHIGHLIGHT_COLOR = '#39E508';constCaptionPage :React .FC <{page :TikTokPage }> = ({page }) => {constframe =useCurrentFrame ();const {fps } =useVideoConfig ();// Current time relative to the start of the sequenceconstcurrentTimeMs = (frame /fps ) * 1000;// Convert to absolute time by adding the page startconstabsoluteTimeMs =page .startMs +currentTimeMs ;return (<AbsoluteFill style ={{justifyContent : 'center',alignItems : 'center',}}><div style ={{fontSize : 80,fontWeight : 'bold',textAlign : 'center',// Preserve whitespace in captionswhiteSpace : 'pre',}}>{page .tokens .map ((token ) => {constisActive =token .fromMs <=absoluteTimeMs &&token .toMs >absoluteTimeMs ;return (<span key ={token .fromMs }style ={{color :isActive ?HIGHLIGHT_COLOR : 'white',}}>{token .text }</span >);})}</div ></AbsoluteFill >);};
Full example
Show full example
Full captioned video exampletsximport {useState ,useEffect ,useCallback ,useMemo } from 'react';import {AbsoluteFill ,Sequence ,staticFile ,useCurrentFrame ,useDelayRender ,useVideoConfig } from 'remotion';import {createTikTokStyleCaptions } from '@remotion/captions';import type {Caption ,TikTokPage } from '@remotion/captions';constSWITCH_CAPTIONS_EVERY_MS = 1200;constHIGHLIGHT_COLOR = '#39E508';constCaptionPage :React .FC <{page :TikTokPage }> = ({page }) => {constframe =useCurrentFrame ();const {fps } =useVideoConfig ();constcurrentTimeMs = (frame /fps ) * 1000;constabsoluteTimeMs =page .startMs +currentTimeMs ;return (<AbsoluteFill style ={{justifyContent : 'center',alignItems : 'center',}}><div style ={{fontSize : 80,fontWeight : 'bold',textAlign : 'center',whiteSpace : 'pre',}}>{page .tokens .map ((token ) => {constisActive =token .fromMs <=absoluteTimeMs &&token .toMs >absoluteTimeMs ;return (<span key ={token .fromMs }style ={{color :isActive ?HIGHLIGHT_COLOR : 'white',}}>{token .text }</span >);})}</div ></AbsoluteFill >);};export constCaptionedVideo :React .FC = () => {const [captions ,setCaptions ] =useState <Caption [] | null>(null);const {delayRender ,continueRender ,cancelRender } =useDelayRender ();const [handle ] =useState (() =>delayRender ());const {fps } =useVideoConfig ();constfetchCaptions =useCallback (async () => {try {constresponse = awaitfetch (staticFile ('captions.json'));constdata = awaitresponse .json ();setCaptions (data );continueRender (handle );} catch (e ) {cancelRender (e );}}, [continueRender ,cancelRender ,handle ]);useEffect (() => {fetchCaptions ();}, [fetchCaptions ]);const {pages } =useMemo (() => {returncreateTikTokStyleCaptions ({captions :captions ?? [],combineTokensWithinMilliseconds :SWITCH_CAPTIONS_EVERY_MS ,});}, [captions ]);return (<AbsoluteFill style ={{backgroundColor : 'black'}}>{pages .map ((page ,index ) => {constnextPage =pages [index + 1] ?? null;conststartFrame = (page .startMs / 1000) *fps ;constendFrame =Math .min (nextPage ? (nextPage .startMs / 1000) *fps :Infinity ,startFrame + (SWITCH_CAPTIONS_EVERY_MS / 1000) *fps );constdurationInFrames =endFrame -startFrame ;if (durationInFrames <= 0) {return null;}return (<Sequence key ={index }from ={startFrame }durationInFrames ={durationInFrames }><CaptionPage page ={page } /></Sequence >);})}</AbsoluteFill >);};
Next steps
You can customize the appearance of your captions:
- Use
fitText()from@remotion/layout-utilsto automatically scale text to fit the video width - Add animations for enter/exit effects
- Apply CSS text stroke for better visibility:
tsx<divstyle={{WebkitTextStroke: '4px black',paintOrder: 'stroke',}}>{text}</div>
See also
- Transcribing audio - Generate captions from audio
Caption- The caption data structurecreateTikTokStyleCaptions()- API reference<Sequence>- Sequence component reference