Customization

This section covers how to leverage its compound component pattern, theming system, and gesture controls.

react-native-video-toolkit is designed for high customizability, allowing you to create unique video player experiences. This section covers how to leverage its compound component pattern, theming system, and gesture controls.

Compound Components

The VideoPlayer component uses a compound component pattern, meaning it exposes several sub-components that you can arrange and style as needed. This gives you full control over the player's UI layout.

The compound component pattern allows you to compose custom layouts while maintaining consistent behavior and state management.

Custom Player UI Example

-0s
+0s
0:00
/
0:00
import React from 'react';
import { View, StyleSheet } from 'react-native';
import {
  VideoPlayer,
  PlayButton,
  ProgressBar,
  TimeDisplay,
  FullscreenButton,
  MuteButton,
  VolumeControl,
  LoadingSpinner,
  SettingsButton,
} from 'react-native-video-toolkit';

export const CustomPlayerUI = () => {
  return (
    <VideoPlayer.Controls style={styles.controlsContainer}>
      {/* Top controls */}
      <View style={styles.topControls}>
        <MuteButton />
        <VolumeControl />
        <View style={{ flex: 1 }} /> {/* Spacer */}
        <FullscreenButton />
        <SettingsButton />
        {/* Use Menu for settings sheet */}
      </View>

      {/* Middle controls (e.g., loading spinner) */}
      <View style={styles.middleControls}>
        <LoadingSpinner />
      </View>

      {/* Bottom controls */}
      <View style={styles.bottomControls}>
        <View style={{ flexDirection: 'row' }}>
          <PlayButton />
          <TimeDisplay />
        </View>
        <ProgressBar />
      </View>
    </VideoPlayer.Controls>
  );
};

export const CustomizationExample = () => {
  const videoSource = {
    uri: '/test.mp4',
  };

  return (
    <VideoPlayer containerStyle={styles.videoPlayer} source={videoSource}>
      <CustomPlayerUI />
    </VideoPlayer>
  );
};

const styles = StyleSheet.create({
  controlsContainer: {
    justifyContent: 'space-between',
  },
  topControls: {
    flexDirection: 'row',
    padding: 16,
    alignItems: 'center',
  },
  middleControls: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  bottomControls: {
    flexDirection: 'column',
    padding: 16,
  },
  videoPlayer: {
    width: '100%',
    height: '100vh', // for web, take full height of the viewport, since the example is been rendered on web
  },
});

export default CustomizationExample;

Theming

The library provides a flexible theming system to easily customize colors, fonts, sizing, and other visual elements. Custom themes should be of type Theme.

Choose a Theme:

Default Dark
Netflix Style
YouTube Style
-0s
+0s
Themed Player Demo
Using Default Dark theme
0:00
/
0:00
import React, { useState } from 'react';
import { View, StyleSheet, TouchableOpacity } from 'react-native';
import { VideoPlayer, VideoProvider, DefaultLayout } from 'react-native-video-toolkit';
import { Badge } from '../ui/badge';

// Define multiple themes for demonstration
const themes = {
  default: {
    name: 'Default Dark',
    colors: {
      primary: '#007AFF',
      secondary: '#5856D6',
      accent: '#FF9500',
      background: '#221F1F',
      overlay: 'rgba(0,0,0,0.7)',
      text: '#FFFFFF',
      error: '#FF3B30',
      success: '#34C759',
      border: '#333333',
    },
    iconSizes: { sm: 16, md: 24, lg: 32 },
    sizing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
    borderRadius: 8,
    fonts: { regular: 'System', medium: 'System', bold: 'System' },
    fontSizes: { sm: 12, md: 16, lg: 20 },
    animations: { fast: 150, normal: 300, slow: 500 },
  },
  netflix: {
    name: 'Netflix Style',
    colors: {
      primary: '#E50914',
      secondary: '#221F1F',
      accent: '#F5F5F1',
      background: '#141414',
      overlay: 'rgba(0,0,0,0.8)',
      text: '#FFFFFF',
      error: '#E50914',
      success: '#46D369',
      border: '#333333',
    },
    iconSizes: { sm: 16, md: 24, lg: 32 },
    sizing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
    borderRadius: 8,
    fonts: { regular: 'System', medium: 'System', bold: 'System' },
    fontSizes: { sm: 12, md: 16, lg: 20 },
    animations: { fast: 150, normal: 300, slow: 500 },
  },
  youtube: {
    name: 'YouTube Style',
    colors: {
      primary: '#FF0000',
      secondary: '#FF4444',
      accent: '#FFFFFF',
      background: '#0F0F0F',
      overlay: 'rgba(15,15,15,0.8)',
      text: '#FFFFFF',
      error: '#FF0000',
      success: '#00FF00',
      border: '#303030',
    },
    iconSizes: { sm: 16, md: 24, lg: 32 },
    sizing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
    borderRadius: 8,
    fonts: { regular: 'System', medium: 'System', bold: 'System' },
    fontSizes: { sm: 12, md: 16, lg: 20 },
    animations: { fast: 150, normal: 300, slow: 500 },
  },
};

type ThemeKey = keyof typeof themes;

export const ThemeSelectorExample = () => {
  const [selectedTheme, setSelectedTheme] = useState<ThemeKey>('default');

  const videoSource = {
    uri: '/test.mp4',
  };

  const currentTheme = themes[selectedTheme];

  return (
    // On native use <View style={styles.container}> instead of div
    <View style={styles.container}>
      {/* Theme Selector */}
      <View style={styles.themeSelector}>
        {/* On native use <Text> instead of p */}
        <p>Choose a Theme:</p>
        <View style={styles.themeButtons}>
          {(Object.keys(themes) as ThemeKey[]).map((themeKey) => (
            <TouchableOpacity key={themeKey} style={styles.themeButtons} onPress={() => setSelectedTheme(themeKey)}>
              <Badge variant={selectedTheme === themeKey ? 'default' : 'secondary'}>{themes[themeKey].name}</Badge>
            </TouchableOpacity>
          ))}
        </View>
      </View>

      {/* Themed Video Player */}
      <VideoProvider theme={currentTheme}>
        <VideoPlayer source={videoSource} videoStyle={{ height: '50%' }} containerStyle={styles.videoPlayer}>
          <DefaultLayout title="Themed Player Demo" subtitle={`Using ${currentTheme.name} theme`} />
        </VideoPlayer>
      </VideoProvider>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    gap: 20,
    flex: 1,
    width: '100%',
  },
  themeSelector: {
    paddingBottom: 10,
  },
  selectorTitle: {
    fontSize: 16,
    fontWeight: '600',
    marginBottom: 12,
    // color: '#333',
  },
  themeButtons: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    gap: 8,
  },

  videoPlayer: {
    width: '100%',
    height: '50vh',
    borderRadius: 12,
    overflow: 'hidden',
    backgroundColor: '#000',
  },
});

Configuration Options

The VideoProvider and VideoPlayer accept various props to configure their behavior.

VideoProvider Props

PropTypeRequiredDefaultDescription
themeThemeNoCustom theme object for colors, fonts, sizing, etc.
configVideoPlayerConfigNoConfiguration options for player behavior.

Configuration Example

import { VideoPlayer, VideoProvider } from 'react-native-video-toolkit';

<VideoProvider
  config={{
    enableScreenRotation: true,
    onEnterFullscreen: () => console.log('Entered fullscreen'),
    onExitFullscreen: () => console.log('Exited fullscreen'),
    onHideControls: () => console.log('Controls hidden'),
    onShowControls: () => console.log('Controls shown'),
  }}>
  <VideoPlayer source={{ uri: 'https://example.com/video.mp4' }}>
    <CustomPlayerUI />
  </VideoPlayer>
</VideoProvider>;

Custom Tracks Example

import { VideoPlayer, VideoProvider } from 'react-native-video-toolkit';
import type { AudioTrack, VideoTrack } from 'react-native-video';

// Define your custom tracks
const customAudioTracks: AudioTrack[] = [
  { index: 0, title: 'English', language: 'en', type: 'audio/mp4a-latm' },
  { index: 1, title: 'Spanish', language: 'es', type: 'audio/mp4a-latm' },
  { index: 2, title: 'French', language: 'fr', type: 'audio/mp4a-latm' },
];

const customVideoTracks: VideoTrack[] = [
  { index: 0, width: 1920, height: 1080, bitrate: 5000000, codecs: 'avc1.640028' },
  { index: 1, width: 1280, height: 720, bitrate: 2500000, codecs: 'avc1.640028' },
  { index: 2, width: 854, height: 480, bitrate: 1000000, codecs: 'avc1.640028' },
];

<VideoProvider
  config={{
    useCustomAudioTracks: true, // Enable custom audio tracks
    useCustomVideoTracks: true, // Enable custom video tracks
  }}>
  <VideoPlayer
    source={{ uri: 'https://example.com/video.m3u8' }}
    customAudioTracks={customAudioTracks}
    customVideoTracks={customVideoTracks}>
    <CustomPlayerUI />
  </VideoPlayer>
</VideoProvider>;

Gestures

The VideoPlayer includes built-in gesture support for common interactions like double-tapping to seek and tapping to toggle controls. You can also pass gestureProps to extend behavior. This prop accepts GestureHandlerProps.

Gestures Example

import { VideoPlayer, DefaultLayout } from 'react-native-video-toolkit';
import { StyleSheet } from 'react-native';

<VideoPlayer
  source={{ uri: 'https://example.com/video.mp4' }}
  containerStyle={styles.videoPlayer}
  gestureProps={{
    onLeftVerticalPan(e) {
      console.log('left pan', e);
    },
    onRightVerticalPan(e) {
      console.log('right pan', e);
    },
    onGlobalVerticalPan(e) {
      console.log('global pan', e);
    },
  }}>
  <DefaultLayout />
</VideoPlayer>;

const styles = StyleSheet.create({
  videoPlayer: {
    width: '100%',
    height: 200,
  },
});