Skip to main content
The <Subtitles /> component renders animated subtitles based on timing data and styling configuration. It supports API-based configuration fetching and custom JSON styling.

Installation

Install the package in your React application:
npm install @overlap.ai/react-video-subtitles

Basic Usage

import { Subtitles } from '@overlap.ai/react-video-subtitles';

function MyComponent() {
  return (
    <Subtitles
      words={words}
      currentTime={currentTime}
      width={1920}
      height={1080}
      fps={30}
      apiKey="your-api-key"
      companyId="your-company-id"
      clipId="your-clip-id"
    />
  );
}
The component fetches styling configuration automatically when you provide apiKey, companyId, and clipId. You can also override with a custom styleConfig prop if needed.

Required Props

PropTypeDescription
wordsArray<Word>Array of word objects with timing data. Included in Clip object
currentTimenumberCurrent playback time in seconds
widthnumberCanvas width in pixels - use standard dimensions (see below)
heightnumberCanvas height in pixels - use standard dimensions (see below)
fpsnumberFrames per second for animation
Use standard dimensions, not your actual video size. The Subtitles component renders as a transparent overlay that sits on top of your video element with pointer passthrough enabled, so click events pass through to the video controls below.

How Width and Height Work

The width and height props control how subtitle font sizes are calculated. Do not change these based on your video’s actual dimensions. Instead, use these standard values: Standard Dimensions to Use:
  • Horizontal videos (16:9): width={1920} height={1080}
  • Vertical videos (9:16): width={1080} height={1920}
These standard dimensions ensure font sizes work correctly without any complex calculations. Even if your video is 720p, 4K, or any other size, always use these standard dimensions. Key Points:
  • Always use standard dimensions: 1920x1080 for horizontal, 1080x1920 for vertical
  • Don’t match actual video size: The dimensions are for font scaling, not video matching
  • Component positioning: Place the Subtitles component over the video in your HTML structure with the same size
  • Transparent overlay: The component is fully transparent except for the subtitle text
  • Pointer passthrough: Mouse clicks and interactions pass through to the video below
Example Setup (Horizontal Video):
<div style={{ position: 'relative', width: '100%' }}>
  {/* Video element - can be any size */}
  <video 
    src="your-video.mp4"
    style={{ width: '100%', height: 'auto' }}
  />
  
  {/* Subtitles component overlay - same HTML size as video */}
  <div style={{ 
    position: 'absolute', 
    top: 0, 
    left: 0, 
    width: '100%', 
    height: '100%',
    pointerEvents: 'none' 
  }}>
    <Subtitles
      words={words}
      currentTime={currentTime}
      width={1920}
      height={1080}
      fps={30}
      apiKey="your-api-key"
      companyId="your-company-id"
      clipId="your-clip-id"
    />
  </div>
</div>
The font size calculations are optimized for 1920x1080 (horizontal) and 1080x1920 (vertical). Using these standard dimensions means you don’t need to adjust fontSize values based on your video’s actual resolution.

Word Object Structure

Pass through the word array returned in the Clip object via transcriptUrl. Each word object in the words array should follow this structure:
interface Word {
  text: string;      // The word to display
  start: number;     // Start time in seconds
  end: number;       // End time in seconds
  speaker?: number;  // Optional speaker identifier
}
Example
const words = [
  { text: "Hello", start: 0, end: 1, speaker: 1 },
  { text: "world", start: 1, end: 2, speaker: 1 },
  { text: "This", start: 2, end: 2.5, speaker: 0 },
  { text: "is", start: 2.5, end: 2.75, speaker: 0 },
  { text: "a", start: 2.75, end: 3, speaker: 0 },
  { text: "test", start: 3, end: 3.5, speaker: 0 },
];

Optional Props

PropTypeDefaultDescription
styleConfigStyleConfignullStyling configuration object
xnumberwidth/2Horizontal position
ynumberheight/2Vertical position
maxCharsPerLinenumber12Maximum characters per line
lineCountnumber1Number of subtitle lines
highlightWordsColorstring-Color for highlighted words
currentFramenumber-Current frame number

API Configuration

The component can fetch styling configuration from your API endpoint. Provide these three props to enable API mode:
PropTypeDescription
apiKeystringYour API authentication key
companyIdstringYour company identifier
clipIdstringThe clip identifier
When all three API props are provided, the component will automatically fetch the styleConfig from your server, overriding any manually provided styleConfig.
Example with API
<Subtitles
  words={words}
  currentTime={currentTime}
  width={1920}
  height={1080}
  fps={30}
  apiKey="your-api-key"
  companyId="your-company-id"
  clipId="your-clip-id"
/>

StyleConfig Object

The styleConfig prop controls the visual appearance of subtitles. For detailed documentation on all available properties, see:

StyleConfig Properties

Complete reference for all styling options including text properties, effects, backgrounds, animations, and more.
Quick Example:
{
  "fontFamily": "Arial",
  "fontSize": "24px",
  "fontWeight": "bold",
  "fontColor": "#ffffff",
  "backgroundColor": "#000000",
  "backgroundOpacity": 0.7,
  "animationStyle": "fade"
}

Hooks

The package includes two powerful hooks for managing subtitle configurations programmatically.

useUpdatePersistentSubtitleConfig

Update subtitle configuration in the database. Import:
import { useUpdatePersistentSubtitleConfig } from '@overlap.ai/react-video-subtitles';
Usage:
const { updateConfig } = useUpdatePersistentSubtitleConfig({
  apiKey: 'your-api-key',
  companyId: 'your-company-id',
  clipId: 'your-clip-id'
});

// Update specific configuration properties
await updateConfig({
  fontSize: '32px',
  fontColor: '#FF0000',
  fontFamily: 'Arial'
});
Parameters:
  • apiKey (string, required) - Your API authentication key
  • companyId (string, required) - Your company identifier
  • clipId (string, required) - The clip identifier
Returns:
  • updateConfig (function) - Async function that accepts a partial StyleConfig object and updates the database
Features:
  • Merges with existing config (only updates specified fields)
  • Preserves all other configuration values
  • Returns a promise that resolves when the update is complete
Example Use Cases:
// Change font size
await updateConfig({ fontSize: '28px' });

// Update multiple properties
await updateConfig({
  fontFamily: 'Helvetica',
  fontColor: '#00FF00',
  backgroundColor: '#000000',
  backgroundOpacity: 0.8
});

// Change animation style
await updateConfig({ animationStyle: 'spring-word' });

useRefreshSubtitlesComponent

Refresh subtitle configuration from the database. Import:
import { useRefreshSubtitlesComponent } from '@overlap.ai/react-video-subtitles';
Usage:
const { refresh, isRefreshing, lastRefreshedConfig } = useRefreshSubtitlesComponent({
  apiKey: 'your-api-key',
  companyId: 'your-company-id',
  clipId: 'your-clip-id',
  onConfigRefreshed: (config) => {
    console.log('Config refreshed:', config);
  }
});

// Refresh the configuration
const newConfig = await refresh();
Parameters:
  • apiKey (string, required) - Your API authentication key
  • companyId (string, required) - Your company identifier
  • clipId (string, required) - The clip identifier
  • onConfigRefreshed (function, optional) - Callback fired when config is successfully refreshed
Returns:
  • refresh (function) - Async function that fetches the latest config from the database
  • isRefreshing (boolean) - Loading state indicator
  • lastRefreshedConfig (StyleConfig | null) - The most recently fetched configuration
Best Practice: For optimal results when refreshing, temporarily unmount the Subtitles component to ensure a clean re-render with the new configuration:
const [refreshTrigger, setRefreshTrigger] = useState(0);
const [isRefreshingComponent, setIsRefreshingComponent] = useState(false);

const { refresh } = useRefreshSubtitlesComponent({
  apiKey,
  companyId,
  clipId,
  onConfigRefreshed: () => {
    setRefreshTrigger(prev => prev + 1);
    setTimeout(() => setIsRefreshingComponent(false), 100);
  }
});

const handleRefresh = async () => {
  setIsRefreshingComponent(true);
  await refresh();
};

return (
  <>
    <button onClick={handleRefresh}>Refresh</button>
    {!isRefreshingComponent && (
      <Subtitles
        key={refreshTrigger}
        words={words}
        currentTime={currentTime}
        width={1920}
        height={1080}
        fps={30}
        apiKey={apiKey}
        companyId={companyId}
        clipId={clipId}
      />
    )}
  </>
);

Configuration Priority

The component follows this priority order for configuration:
1

API Config (Highest Priority)

If apiKey, companyId, and clipId are all provided, the component fetches configuration from the API endpoint.
2

Custom StyleConfig

If styleConfig prop is provided and no API credentials are given.
3

No Display

If neither API credentials nor styleConfig is provided, the component will not render anything.
The component requires EITHER API credentials (all three) OR a custom styleConfig to render. Providing none will result in no subtitle display.

Complete TypeScript Example

import React, { useState, useEffect } from 'react';
import { Subtitles } from '@overlap.ai/react-video-subtitles';

interface Word {
  text: string;
  start: number;
  end: number;
  speaker?: number;
}

function SubtitlePlayer() {
  const [currentTime, setCurrentTime] = useState(0);
  
  const words: Word[] = [
    { text: "Welcome", start: 0, end: 0.5, speaker: 0 },
    { text: "to", start: 0.5, end: 0.8, speaker: 0 },
    { text: "our", start: 0.8, end: 1.2, speaker: 0 },
    { text: "presentation", start: 1.2, end: 2.0, speaker: 0 },
  ];
  
  // Simulate time updates
  useEffect(() => {
    const interval = setInterval(() => {
      setCurrentTime(prev => (prev + 0.033) % 2.5);
    }, 33);
    
    return () => clearInterval(interval);
  }, []);
  
  return (
    <div style={{ position: 'relative', width: 1920, height: 1080 }}>
      <Subtitles
        words={words}
        currentTime={currentTime}
        currentFrame={Math.floor(currentTime * 30)}
        width={1920}
        height={1080}
        fps={30}
        maxCharsPerLine={15}
        lineCount={1}
        apiKey="your-api-key"
        companyId="your-company-id"
        clipId="your-clip-id"
      />
    </div>
  );
}

Troubleshooting

Subtitles Not Showing

No configuration provided:
  • Ensure either API credentials (all three: apiKey, companyId, clipId) OR a custom styleConfig is provided
  • Component will not render without valid configuration
Invalid timing:
  • Check that currentTime is within the range of your words’ start/end times
  • Verify words array has proper start/end values in seconds

API Errors

401 Unauthorized:
  • Verify your API key is correct
  • Check that the API key has proper permissions
404 Not Found:
  • Verify the companyId and clipId exist
  • Check for typos in the IDs

Styling Issues

Text size wrong:
  • Always use standard dimensions: width={1920} height={1080} for horizontal videos, width={1080} height={1920} for vertical videos
  • Adjust the fontSize value in your styleConfig (e.g., change from "24" to "32")
Text position wrong:
  • Adjust x and y props for precise positioning
  • Verify the Subtitles component is positioned over the video in your HTML/CSS
  • Make sure you’re using standard dimensions
Animation not smooth:
  • Ensure fps matches your expected framerate
  • Verify currentTime updates smoothly (typically 30-60 times per second)

API Reference Summary

interface SubtitlesProps {
  // Required
  words: Word[];
  currentTime: number;
  width: number;
  height: number;
  fps: number;
  
  // Optional - Styling
  styleConfig?: StyleConfig;
  x?: number;
  y?: number;
  maxCharsPerLine?: number;
  lineCount?: number;
  highlightWordsColor?: string;
  currentFrame?: number;
  
  // Optional - API Config
  apiKey?: string;
  companyId?: string;
  clipId?: string;
}
For production use, consider implementing error boundaries around the Subtitles component to gracefully handle API failures or invalid configurations.