Event Handling
React Native Video provides comprehensive event callbacks to monitor and respond to video playback states, errors, and user interactions. This guide covers all available events and how to use them effectively.
Event Types
Events are divided into two categories:
- Player Events: Fired by the
VideoPlayer instance
- View Events: Fired by the
VideoView component
Adding Event Listeners
There are two ways to add event listeners:
1. Using the Setup Function
Pass a setup function to useVideoPlayer:
import { useVideoPlayer } from 'react-native-video';
const player = useVideoPlayer('https://example.com/video.mp4', (player) => {
player.addEventListener('onLoad', (data) => {
console.log('Video loaded:', data.duration, 'seconds');
});
player.addEventListener('onError', (error) => {
console.error('Playback error:', error.message);
});
});
2. Using addEventListener Directly
import { useVideoPlayer } from 'react-native-video';
import { useEffect } from 'react';
const player = useVideoPlayer('https://example.com/video.mp4');
useEffect(() => {
const subscription = player.addEventListener('onLoad', (data) => {
console.log('Video loaded:', data.duration);
});
return () => {
subscription.remove();
};
}, []);
Always clean up event listeners by calling subscription.remove() or using the cleanup function in useEffect.
Player Events
Lifecycle Events
onLoadStart
Fired when the video starts loading.
player.addEventListener('onLoadStart', (data) => {
console.log('Source type:', data.sourceType); // 'local' or 'network'
console.log('Source:', data.source);
});
Payload:
sourceType: 'local' | 'network'
source: VideoPlayerSource object
onLoad
Fired when the video metadata is loaded and ready to play.
player.addEventListener('onLoad', (data) => {
console.log('Duration:', data.duration);
console.log('Dimensions:', data.width, 'x', data.height);
console.log('Orientation:', data.orientation);
console.log('Current time:', data.currentTime);
});
Payload:
currentTime: number - Current time in seconds
duration: number - Total duration in seconds (NaN if unknown)
width: number - Video width in pixels
height: number - Video height in pixels
orientation: VideoOrientation - Video orientation
onReadyToDisplay
Fired when the video is ready to be displayed on screen.
player.addEventListener('onReadyToDisplay', () => {
console.log('Video ready to display');
});
Playback Events
onPlaybackStateChange
Fired when playback state changes.
player.addEventListener('onPlaybackStateChange', (data) => {
console.log('Is playing:', data.isPlaying);
console.log('Is buffering:', data.isBuffering);
});
Payload:
isPlaying: boolean
isBuffering: boolean
onStatusChange
Fired when the player status changes.
player.addEventListener('onStatusChange', (status) => {
console.log('Status:', status); // 'playing', 'paused', 'buffering', etc.
});
Payload:
status: VideoPlayerStatus
onProgress
Fired periodically during playback to report progress.
player.addEventListener('onProgress', (data) => {
console.log('Current time:', data.currentTime);
console.log('Buffer duration:', data.bufferDuration);
});
Payload:
currentTime: number - Current playback time in seconds
bufferDuration: number - Time that player can play with buffer
onSeek
Fired when a seek operation completes.
player.addEventListener('onSeek', (seekTime) => {
console.log('Seeked to:', seekTime, 'seconds');
});
Payload:
seekTime: number - Time seeked to in seconds
onEnd
Fired when video playback reaches the end.
player.addEventListener('onEnd', () => {
console.log('Video finished');
});
onPlaybackRateChange
Fired when the playback rate changes.
player.addEventListener('onPlaybackRateChange', (rate) => {
console.log('Playback rate:', rate); // 0.5, 1.0, 2.0, etc.
});
Payload:
rate: number - Current playback rate
Buffer Events
onBuffer
Fired when buffering state changes.
player.addEventListener('onBuffer', (buffering) => {
if (buffering) {
console.log('Buffering...');
} else {
console.log('Buffering complete');
}
});
Payload:
Bandwidth Events
onBandwidthUpdate
Fired when the video bitrate changes (adaptive streaming).
player.addEventListener('onBandwidthUpdate', (data) => {
console.log('Bitrate:', data.bitrate, 'bps');
console.log('Resolution:', data.width, 'x', data.height); // Android only
});
Payload:
bitrate: number - Current bitrate in bits per second
width?: number - Video width (Android only)
height?: number - Video height (Android only)
Volume Events
onVolumeChange
Fired when volume or mute state changes.
player.addEventListener('onVolumeChange', (data) => {
console.log('Volume:', data.volume); // 0.0 to 1.0
console.log('Muted:', data.muted);
});
Payload:
volume: number - Volume level (0.0 = 0%, 1.0 = 100%)
muted: boolean - Whether the player is muted
Text Track Events
onTrackChange
Fired when the selected subtitle track changes.
player.addEventListener('onTrackChange', (track) => {
if (track) {
console.log('Track changed to:', track.label);
} else {
console.log('Subtitles disabled');
}
});
Payload:
track: TextTrack | null - Selected track or null
onTextTrackDataChanged
Fired when subtitle text is displayed.
player.addEventListener('onTextTrackDataChanged', (texts) => {
console.log('Current subtitles:', texts);
});
Payload:
texts: string[] - Array of currently displayed subtitle strings
Fired when timed metadata is received from the stream.
player.addEventListener('onTimedMetadata', (metadata) => {
metadata.metadata.forEach((item) => {
console.log('Key:', item.identifier);
console.log('Value:', item.value);
});
});
Payload:
metadata: Array of { identifier: string, value: string }
onAudioBecomingNoisy (Android)
Fired when audio becomes noisy (e.g., headphones unplugged).
player.addEventListener('onAudioBecomingNoisy', () => {
console.log('Audio becoming noisy - pausing');
player.pause();
});
onAudioFocusChange (Android)
Fired when audio focus changes.
player.addEventListener('onAudioFocusChange', (hasAudioFocus) => {
console.log('Audio focus:', hasAudioFocus);
});
Payload:
onExternalPlaybackChange (iOS)
Fired when external playback state changes (e.g., AirPlay).
player.addEventListener('onExternalPlaybackChange', (externalPlaybackActive) => {
console.log('External playback:', externalPlaybackActive);
});
Payload:
externalPlaybackActive: boolean
Error Events
onError
Fired when a playback error occurs.
player.addEventListener('onError', (error) => {
console.error('Error code:', error.code);
console.error('Error message:', error.message);
});
Payload:
error: VideoRuntimeError with code and message
View Events
View events are passed as props to the VideoView component:
onPictureInPictureChange
<VideoView
player={player}
onPictureInPictureChange={(isActive) => {
console.log('PiP active:', isActive);
}}
/>
onFullscreenChange
<VideoView
player={player}
onFullscreenChange={(isFullscreen) => {
console.log('Fullscreen:', isFullscreen);
}}
/>
willEnterFullscreen / willExitFullscreen
<VideoView
player={player}
willEnterFullscreen={() => {
console.log('About to enter fullscreen');
}}
willExitFullscreen={() => {
console.log('About to exit fullscreen');
}}
/>
willEnterPictureInPicture / willExitPictureInPicture
<VideoView
player={player}
willEnterPictureInPicture={() => {
console.log('About to enter PiP');
}}
willExitPictureInPicture={() => {
console.log('About to exit PiP');
}}
/>
onControlsVisibleChange
player.addEventListener('onControlsVisibleChange', (visible) => {
console.log('Controls visible:', visible);
});
Payload:
Complete Event Monitoring Example
import { VideoView, useVideoPlayer } from 'react-native-video';
import { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
export function EventMonitor() {
const [events, setEvents] = useState<string[]>([]);
const logEvent = (name: string, data?: any) => {
const message = `${name}: ${data ? JSON.stringify(data) : ''}`;
setEvents(prev => [message, ...prev].slice(0, 10)); // Keep last 10 events
};
const player = useVideoPlayer('https://www.w3schools.com/html/mov_bbb.mp4', (player) => {
// Lifecycle
player.addEventListener('onLoadStart', (data) => {
logEvent('onLoadStart', data.sourceType);
});
player.addEventListener('onLoad', (data) => {
logEvent('onLoad', { duration: data.duration });
});
player.addEventListener('onReadyToDisplay', () => {
logEvent('onReadyToDisplay');
});
// Playback
player.addEventListener('onPlaybackStateChange', (data) => {
logEvent('onPlaybackStateChange', data);
});
player.addEventListener('onProgress', (data) => {
logEvent('onProgress', { time: data.currentTime.toFixed(1) });
});
player.addEventListener('onSeek', (time) => {
logEvent('onSeek', time);
});
player.addEventListener('onEnd', () => {
logEvent('onEnd');
});
// Buffer
player.addEventListener('onBuffer', (buffering) => {
logEvent('onBuffer', buffering);
});
// Error
player.addEventListener('onError', (error) => {
logEvent('onError', error.message);
});
});
return (
<View style={styles.container}>
<VideoView
player={player}
style={styles.video}
controls
onPictureInPictureChange={(isActive) => {
logEvent('onPictureInPictureChange', isActive);
}}
onFullscreenChange={(isFullscreen) => {
logEvent('onFullscreenChange', isFullscreen);
}}
/>
<View style={styles.eventLog}>
<Text style={styles.title}>Event Log:</Text>
{events.map((event, index) => (
<Text key={index} style={styles.eventText}>
{event}
</Text>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
video: {
width: '100%',
height: 300,
},
eventLog: {
flex: 1,
padding: 10,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
},
eventText: {
fontSize: 12,
fontFamily: 'monospace',
marginBottom: 4,
},
});
Best Practices
1. Clean Up Event Listeners
Always remove listeners when components unmount:
useEffect(() => {
const subscription = player.addEventListener('onProgress', (data) => {
// Handle progress
});
return () => {
subscription.remove();
};
}, []);
2. Handle Errors Gracefully
player.addEventListener('onError', (error) => {
// Log error for debugging
console.error('Playback error:', error);
// Show user-friendly message
Alert.alert('Video Error', 'Unable to play this video. Please try again.');
// Try to recover or provide alternative
player.replaceSourceAsync(fallbackVideoUrl);
});
3. Debounce Frequent Events
For events that fire frequently (like onProgress), consider debouncing:
import { debounce } from 'lodash';
const handleProgress = debounce((data) => {
// Update UI
setCurrentTime(data.currentTime);
}, 100);
player.addEventListener('onProgress', handleProgress);
4. Use onLoad for Initialization
Wait for onLoad before accessing video metadata:
player.addEventListener('onLoad', (data) => {
// Safe to access duration, dimensions, etc.
console.log('Video is', data.duration, 'seconds long');
// Safe to get text tracks
const tracks = player.getAvailableTextTracks();
});
5. Monitor Buffering for UX
Show loading indicators during buffering:
const [isBuffering, setIsBuffering] = useState(false);
player.addEventListener('onBuffer', (buffering) => {
setIsBuffering(buffering);
});
// In render:
{isBuffering && <ActivityIndicator />}
Troubleshooting
Events Not Firing
- Ensure the player has loaded (
onLoad must fire first for most events)
- Check that event listeners are added before the event occurs
- Verify the event name is spelled correctly
Memory Leaks
- Always clean up event listeners in
useEffect cleanup functions
- Remove listeners when the player is released
Duplicate Events
- Avoid adding the same listener multiple times
- Use
useEffect with proper dependencies to control listener registration
Event Reference Summary
| Event | When Fired | Payload |
|---|
onLoadStart | Video starts loading | { sourceType, source } |
onLoad | Metadata loaded | { duration, width, height, ... } |
onReadyToDisplay | Ready to display | - |
onPlaybackStateChange | Playback state changes | { isPlaying, isBuffering } |
onStatusChange | Player status changes | VideoPlayerStatus |
onProgress | During playback | { currentTime, bufferDuration } |
onSeek | Seek completes | number (seek time) |
onEnd | Video ends | - |
onBuffer | Buffering state changes | boolean |
onBandwidthUpdate | Bitrate changes | { bitrate, width?, height? } |
onVolumeChange | Volume/mute changes | { volume, muted } |
onTrackChange | Subtitle track changes | TextTrack | null |
onError | Error occurs | VideoRuntimeError |
Next Steps