Blue Billywig Native Video Player for React Native - iOS AVPlayer and Android ExoPlayer integration
npm install @bluebillywig/react-native-bb-playerNative video player for React Native - powered by Blue Billywig's iOS and Android SDKs.
react-native-bb-player provides a production-ready, native video player component for React Native. It wraps Blue Billywig's native iOS and Android player SDKs, giving you:
- True native playback - iOS AVPlayer and Android ExoPlayer (no WebView)
- Full-featured player - Ads, analytics, Content Protection
- Type-safe API - Full TypeScript support with comprehensive types
- Production ready - Built on Blue Billywig's battle-tested native SDKs
- Easy integration - Simple component-based API with imperative ref methods
| Platform | Requirement | Player Engine |
|----------|-------------|---------------|
| iOS | 12.0+ | AVPlayer |
| Android | API 21+ (5.0+) | ExoPlayer |
| React Native | 0.73+ | Old & New Architecture (TurboModules) |
| Expo | SDK 51+ | With config plugin (optional) |
``bash`
npm install @bluebillywig/react-native-bb-playeror
yarn add @bluebillywig/react-native-bb-player
#### iOS Setup
`bash`
cd ios && pod install && cd ..
#### Android Setup
No additional setup needed - autolinking handles it automatically.
#### Rebuild Your App
`bashiOS
npx react-native run-ios
$3
> Note: This SDK requires native code and cannot run in Expo Go. You must use a development build.
`bash
npx expo install @bluebillywig/react-native-bb-player
`Add the config plugin to your
app.json:`json
{
"expo": {
"plugins": [
"@bluebillywig/react-native-bb-player"
]
}
}
`Then build your development app:
`bash
npx expo prebuild
npx expo run:ios
or
npx expo run:android
`See the Expo Setup Guide for detailed configuration options.
$3
- Expo Setup Guide - Expo configuration and prebuild
- Advertising Guide - Ad integration and VAST/VPAID
- Analytics Guide - Analytics integration and custom statistics
- Shorts Guide - Vertical video player (TikTok-style experience)
- Outstream Guide - Outstream advertising with collapse/expand
- Deep Linking Guide - Open video content via URLs
Quick Start
Here's the simplest way to get started:
`tsx
import React, { useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import { BBPlayerView, type BBPlayerViewMethods } from '@bluebillywig/react-native-bb-player';export default function App() {
const playerRef = useRef(null);
return (
ref={playerRef}
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
options={{
autoPlay: true,
allowCollapseExpand: true,
}}
onDidTriggerPlay={() => console.log('Playing')}
onDidTriggerPause={() => console.log('Paused')}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
},
});
`Usage Examples
$3
Minimal setup to play a video:
`tsx
import { BBPlayerView } from '@bluebillywig/react-native-bb-player';export function BasicPlayer() {
return (
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
options={{ autoPlay: false }}
style={{ flex: 1 }}
/>
);
}
`$3
Control playback programmatically:
`tsx
import React, { useRef } from 'react';
import { View, Button } from 'react-native';
import { BBPlayerView, type BBPlayerViewMethods } from '@bluebillywig/react-native-bb-player';export function ControlledPlayer() {
const playerRef = useRef(null);
const handlePlay = () => {
playerRef.current?.play();
};
const handlePause = () => {
playerRef.current?.pause();
};
const handleSeek = () => {
playerRef.current?.seek(30); // Seek to 30 seconds
};
return (
ref={playerRef}
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
style={{ flex: 1 }}
/>
);
}
`$3
Listen to player events:
`tsx
import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { BBPlayerView, type State, type Phase } from '@bluebillywig/react-native-bb-player';export function EventListenerExample() {
const [playerState, setPlayerState] = useState('IDLE');
const [playerPhase, setPlayerPhase] = useState('INIT');
const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
return (
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
options={{ autoPlay: true }}
style={{ flex: 1 }}
onDidTriggerStateChange={(state) => setPlayerState(state)}
onDidTriggerPhaseChange={(phase) => setPlayerPhase(phase)}
onDidTriggerDurationChange={(dur) => setDuration(dur)}
onDidTriggerTimeUpdate={(time) => setCurrentTime(time)}
onDidTriggerPlay={() => console.log('Playback started')}
onDidTriggerPause={() => console.log('Playback paused')}
onDidTriggerEnded={() => console.log('Playback ended')}
/>
State: {playerState}
Phase: {playerPhase}
Time: {currentTime.toFixed(1)}s / {duration.toFixed(1)}s
);
}
`$3
Load different content without recreating the player:
`tsx
import React, { useRef } from 'react';
import { View, Button } from 'react-native';
import { BBPlayerView, type BBPlayerViewMethods } from '@bluebillywig/react-native-bb-player';export function DynamicLoading() {
const playerRef = useRef(null);
return (
ref={playerRef}
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
style={{ flex: 1 }}
/>
{/ Primary API - loadClip with options /}
title="Load Clip 123"
onPress={() => playerRef.current?.loadClip('123', { autoPlay: true })}
/>
title="Load with Playout"
onPress={() => playerRef.current?.loadClip('456', { playout: 'default', autoPlay: true })}
/>
{/ Legacy API - still supported /}
title="Load ClipList"
onPress={() => playerRef.current?.loadWithClipListId('789', 'user', true)}
/>
);
}
`$3
Launch player in fullscreen mode:
`tsx
import React, { useRef, useEffect } from 'react';
import { BBPlayerView, type BBPlayerViewMethods } from '@bluebillywig/react-native-bb-player';export function FullscreenPlayer() {
const playerRef = useRef(null);
useEffect(() => {
// Enter fullscreen after player is ready
const timer = setTimeout(() => {
playerRef.current?.enterFullscreen();
}, 1000);
return () => clearTimeout(timer);
}, []);
return (
ref={playerRef}
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
options={{
autoPlay: true,
}}
style={{ flex: 1 }}
onDidTriggerFullscreen={() => console.log('Entered fullscreen')}
onDidTriggerRetractFullscreen={() => console.log('Exited fullscreen')}
/>
);
}
`$3
Open the Google Cast device picker to cast video to Chromecast devices:
`tsx
import React, { useRef } from 'react';
import { View, Button } from 'react-native';
import { BBPlayerView, type BBPlayerViewMethods } from '@bluebillywig/react-native-bb-player';export function ChromecastPlayer() {
const playerRef = useRef(null);
const handleCast = () => {
playerRef.current?.showCastPicker();
};
return (
ref={playerRef}
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
style={{ flex: 1 }}
/>
);
}
`> Note: Chromecast functionality is available on both iOS and Android platforms.
API Reference
$3
| Prop | Type | Required | Description |
|------|------|----------|-------------|
|
jsonUrl | string | Yes | Blue Billywig media JSON URL |
| playerId | string | No | Unique identifier for multi-player scenarios |
| jwt | string | No | JWT token for authenticated playback |
| options | Record | No | Player configuration options |
| style | ViewStyle | No | React Native style object |
| enableTimeUpdates | boolean | No | Enable time update events (default: false) |
| Event props | See below | No | Event callback handlers |$3
`typescript
// Playback Control
play(): void
pause(): void
seek(position: number): void
seekRelative(offsetSeconds: number): void// Volume Control
setVolume(volume: number): void
setMuted(muted: boolean): void
// Layout Control
collapse(): void
expand(): void
enterFullscreen(): void
enterFullscreenLandscape(): void
exitFullscreen(): void
// Chromecast
showCastPicker(): void
// Load Content (Primary API)
loadClip(clipId: string, options?: LoadClipOptions): void
// Load Content (Legacy)
loadWithClipId(clipId: string, initiator?: string, autoPlay?: boolean, seekTo?: number): void
loadWithClipListId(clipListId: string, initiator?: string, autoPlay?: boolean, seekTo?: number): void
loadWithProjectId(projectId: string, initiator?: string, autoPlay?: boolean, seekTo?: number): void
loadWithClipJson(clipJson: string, initiator?: string, autoPlay?: boolean, seekTo?: number): void
loadWithClipListJson(clipListJson: string, initiator?: string, autoPlay?: boolean, seekTo?: number): void
loadWithProjectJson(projectJson: string, initiator?: string, autoPlay?: boolean, seekTo?: number): void
loadWithJsonUrl(jsonUrl: string, autoPlay?: boolean): void
// Async Getters (Primary API)
getPlayerState(): Promise // Returns complete player state
// Async Getters (Individual)
getDuration(): Promise
getCurrentTime(): Promise
getMuted(): Promise
getVolume(): Promise
getPhase(): Promise
getState(): Promise
getMode(): Promise
getClipData(): Promise<{ id?: string; title?: string; description?: string; length?: number } | null>
getProjectData(): Promise<{ id?: string; name?: string } | null>
getPlayoutData(): Promise<{ name?: string } | null>
// Cleanup
destroy(): void
autoPlayNextCancel(): void
`$3
`typescript
// Setup & Lifecycle
onDidSetupWithJsonUrl?: (url: string) => void
onDidTriggerApiReady?: () => void
onDidFailWithError?: (error: string) => void// Playback
onDidTriggerPlay?: () => void
onDidTriggerPause?: () => void
onDidTriggerPlaying?: () => void
onDidTriggerEnded?: () => void
onDidTriggerCanPlay?: () => void
onDidTriggerSeeking?: () => void
onDidTriggerSeeked?: (position: number) => void
onDidTriggerStall?: () => void
onDidTriggerTimeUpdate?: (currentTime: number, duration: number) => void
onDidTriggerDurationChange?: (duration: number) => void
onDidTriggerVolumeChange?: (volume: number) => void
// State Changes
onDidTriggerStateChange?: (state: State) => void
onDidTriggerPhaseChange?: (phase: Phase) => void
onDidTriggerModeChange?: (mode: string) => void
// Media Loading
onDidTriggerMediaClipLoaded?: (clip: MediaClip) => void
onDidTriggerMediaClipFailed?: () => void
onDidTriggerProjectLoaded?: (project: Project) => void
// View Events
onDidTriggerViewStarted?: () => void
onDidTriggerViewFinished?: () => void
// Fullscreen
onDidTriggerFullscreen?: () => void
onDidTriggerRetractFullscreen?: () => void
// Layout
onDidRequestCollapse?: () => void
onDidRequestExpand?: () => void
onDidRequestOpenUrl?: (url: string) => void
// Auto-Pause
onDidTriggerAutoPause?: (why: string) => void
onDidTriggerAutoPausePlay?: (why: string) => void
// Ads
onDidTriggerAdLoadStart?: () => void
onDidTriggerAdLoaded?: () => void
onDidTriggerAdStarted?: () => void
onDidTriggerAdFinished?: () => void
onDidTriggerAdNotFound?: () => void
onDidTriggerAdError?: (error: string) => void
onDidTriggerAdQuartile1?: () => void
onDidTriggerAdQuartile2?: () => void
onDidTriggerAdQuartile3?: () => void
onDidTriggerAllAdsCompleted?: () => void
// Custom Statistics (for analytics integration)
onDidTriggerCustomStatistics?: (stats: CustomStatistics) => void
`> Note: See the Analytics Guide for detailed analytics integration examples.
$3
`typescript
// Player state enum
type State = "IDLE" | "LOADING" | "PLAYING" | "PAUSED" | "ERROR";
type Phase = "INIT" | "PRE" | "MAIN" | "POST" | "EXIT";// Options for loadClip()
type LoadClipOptions = {
playout?: string; // Playout name/ID
autoPlay?: boolean; // Auto-play after loading
seekTo?: number; // Seek to position (seconds)
initiator?: string; // Analytics initiator
};
// Complete player state object (returned by getPlayerState())
type BBPlayerState = {
state: State;
phase: Phase;
mode: string | null;
currentTime: number;
duration: number;
muted: boolean;
volume: number;
clip: { id?: string; title?: string; description?: string; length?: number } | null;
project: { id?: string; name?: string } | null;
playout: { name?: string } | null;
};
// Event payload types (for typed event handling)
type BBPlayerEventPayloads = {
play: void;
pause: void;
stateChange: { state: State };
phaseChange: { phase: Phase };
timeUpdate: { currentTime: number; duration: number };
// ... and more
};
`Performance Optimization
$3
By default, the player does not emit
onDidTriggerTimeUpdate events to reduce CPU overhead. Enable only when needed:`tsx
enableTimeUpdates={true}
onDidTriggerTimeUpdate={(time, dur) => {
setCurrentTime(time);
setDuration(dur);
}}
/>
`$3
If you only need the current time occasionally, use the async getter:
`tsx
const handleGetTime = async () => {
const time = await playerRef.current?.getCurrentTime();
console.log('Current time:', time);
};
`$3
Use
getPlayerState() to fetch all player state at once:`tsx
const handleGetState = async () => {
const state = await playerRef.current?.getPlayerState();
if (state) {
console.log(Playing: ${state.state === 'PLAYING'});
console.log(Progress: ${state.currentTime}/${state.duration});
console.log(Clip: ${state.clip?.title});
console.log(Volume: ${state.volume}, Muted: ${state.muted});
}
};
`Overriding Native SDK Versions
$3
- iOS:
~>8.40 (Blue Billywig Native Player Kit)
- Android: 8.42.+ (Blue Billywig Native Player SDK)$3
`ruby
pod 'BlueBillywigNativePlayerKit-iOS', '8.42.0'
`$3
`kotlin
configurations.all {
resolutionStrategy {
force 'com.bluebillywig.bbnativeplayersdk:bbnativeplayersdk:8.42.0'
}
}
`Troubleshooting
$3
1. Ensure the package is installed
2. Run
cd ios && pod install
3. Rebuild the app completely$3
- Check the JSON URL is correct and accessible
- Verify internet connectivity
- Add error handling to debug:
`tsx
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
onDidFailWithError={(error) => console.error('Player error:', error)}
onDidSetupWithJsonUrl={(url) => console.log('Player setup complete:', url)}
/>
`$3
Ensure the player is ready before calling methods:
`tsx
const [isReady, setIsReady] = useState(false); ref={playerRef}
onDidTriggerCanPlay={() => setIsReady(true)}
/>
// Then call methods
if (isReady) {
playerRef.current?.play();
}
`$3
1. Run
cd ios && pod deintegrate && pod install
2. Clean build folder in Xcode$3
1. Run
cd android && ./gradlew clean
2. Ensure JDK 17+ is installed$3
React Native's built-in
SafeAreaView doesn't work consistently across platforms. For reliable safe area handling on both iOS and Android, use react-native-safe-area-context:`bash
npm install react-native-safe-area-context
`Setup:
`tsx
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';function App() {
return (
);
}
`In your screens:
`tsx
import { SafeAreaView } from 'react-native-safe-area-context';function PlayerScreen() {
return (
);
}
`For more control with insets:
`tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';function CustomScreen() {
const insets = useSafeAreaInsets();
return (
);
}
`New Architecture (Fabric & TurboModules)
This package fully supports React Native's New Architecture, including:
- Fabric - The new rendering system
- TurboModules - The new native module system with synchronous access and lazy loading
$3
The package automatically detects which architecture your app uses:
- New Architecture enabled: Uses
TurboModuleRegistry for optimal performance
- Old Architecture: Falls back to NativeModules (no changes needed)$3
#### React Native 0.76+
New Architecture is enabled by default in React Native 0.76 and later.
#### React Native 0.73-0.75
Enable in your app's configuration:
Android (
android/gradle.properties):
`properties
newArchEnabled=true
`iOS (
ios/Podfile):
`ruby
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
`Then rebuild your app:
`bash
iOS
cd ios && pod install && cd ..
npx react-native run-iosAndroid
cd android && ./gradlew clean && cd ..
npx react-native run-android
`$3
Your existing code works with both architectures. The package handles the architecture detection internally:
`tsx
// This works on both Old and New Architecture
import { BBPlayerView } from '@bluebillywig/react-native-bb-player'; ref={playerRef}
jsonUrl="https://demo.bbvms.com/p/default/c/4701337.json"
onDidTriggerPlay={() => console.log('Playing')}
/>
`FAQ
$3
Yes! This package wraps Blue Billywig's production-grade native SDKs used by major media companies.
$3
Yes, the player includes full ad support (VAST, VPAID, etc.). Configure ads in your Blue Billywig playout configuration.
$3
The player supports formats compatible with:
- iOS: AVPlayer (HLS, MP4, M4V, MOV, etc.)
- Android: ExoPlayer (HLS, DASH, MP4, WebM, etc.)
$3
Contact Blue Billywig to get access to your media content. The JSON URL format is:
`
https://{your-domain}.bbvms.com/p/{playout}/c/{clipId}.json
``Full TypeScript API documentation is available at:
https://bluebillywig.github.io/react-native-bb-player/
The documentation is automatically generated from source code and updated on every release.
MIT
---
Built with love by Blue Billywig