Embedded Integration
Embed the channel within your app's existing UI — alongside custom headers, footers, or inside a tab navigator.
With Custom Header
A common pattern: your own header with Home, Search, and Back buttons above the channel.
import React, { useRef, useState } from 'react';
import { View, TouchableOpacity, Text, StyleSheet, SafeAreaView } from 'react-native';
import {
BBChannel,
BBChannelRef,
BBFullscreenPlayer,
BBMediaInfo,
} from '@bluebillywig/react-native-channel';
function EmbeddedChannelScreen() {
const channelRef = useRef<BBChannelRef>(null);
const [media, setMedia] = useState<BBMediaInfo | null>(null);
const [canGoBack, setCanGoBack] = useState(false);
return (
<SafeAreaView style={styles.container}>
{/* Custom header */}
<View style={styles.header}>
{canGoBack ? (
<TouchableOpacity onPress={() => channelRef.current?.goBack()}>
<Text style={styles.headerButton}>← Back</Text>
</TouchableOpacity>
) : (
<View style={styles.headerButton} />
)}
<Text style={styles.headerTitle}>Videos</Text>
<View style={styles.headerActions}>
<TouchableOpacity onPress={() => channelRef.current?.navigateTo('main')}>
<Text style={styles.headerButton}>Home</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => channelRef.current?.search('')}>
<Text style={styles.headerButton}>Search</Text>
</TouchableOpacity>
</View>
</View>
{/* Channel without top safe area (header handles it) */}
<BBChannel
ref={channelRef}
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
safeAreaEdges={[]}
onCanGoBackChange={setCanGoBack}
onMediaRequest={(mediaInfo) => setMedia(mediaInfo)}
style={styles.channel}
/>
{/* Footer */}
<View style={styles.footer}>
<Text style={styles.footerText}>Powered by Blue Billywig</Text>
</View>
<BBFullscreenPlayer
media={media}
visible={media !== null}
onClose={() => setMedia(null)}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#1a1a1a',
},
headerTitle: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
headerActions: {
flexDirection: 'row',
gap: 12,
},
headerButton: {
color: '#0af',
fontSize: 16,
minWidth: 50,
},
channel: {
flex: 1,
},
footer: {
padding: 8,
alignItems: 'center',
backgroundColor: '#1a1a1a',
},
footerText: {
color: '#666',
fontSize: 12,
},
});
When you provide your own header, set safeAreaEdges={[]} on BBChannel to avoid double padding. Your SafeAreaView wrapper already handles the safe area.
Dynamic Height
If the channel doesn't fill the entire screen, you can give it a fixed or dynamic height:
function PartialScreenChannel() {
const [media, setMedia] = useState<BBMediaInfo | null>(null);
return (
<View style={styles.container}>
<Text style={styles.heading}>Featured Content</Text>
<BBChannel
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
safeAreaEdges={[]}
onMediaRequest={(mediaInfo) => setMedia(mediaInfo)}
style={{ height: 400 }}
/>
<Text style={styles.heading}>Other Content</Text>
<BBFullscreenPlayer
media={media}
visible={media !== null}
onClose={() => setMedia(null)}
/>
</View>
);
}
A fixed-height channel is scrollable internally. The channel handles its own scrolling, so avoid nesting it inside a ScrollView.
Tab Navigator Integration
Using React Navigation's bottom tabs:
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer, useFocusEffect } from '@react-navigation/native';
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen
name="Videos"
component={VideoTabScreen}
options={{ headerShown: false }}
/>
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
function VideoTabScreen() {
const channelRef = useRef<BBChannelRef>(null);
const [media, setMedia] = useState<BBMediaInfo | null>(null);
const canGoBackRef = useRef(false);
// Handle back button only when this tab is focused
useFocusEffect(
React.useCallback(() => {
const handler = BackHandler.addEventListener('hardwareBackPress', () => {
if (media) {
setMedia(null);
return true;
}
if (canGoBackRef.current) {
channelRef.current?.goBack();
return true;
}
return false; // Let tab navigator handle it
});
return () => handler.remove();
}, [media])
);
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#000' }}>
<BBChannel
ref={channelRef}
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
safeAreaEdges={[]}
onCanGoBackChange={(val) => {
canGoBackRef.current = val;
}}
onMediaRequest={(mediaInfo) => setMedia(mediaInfo)}
style={{ flex: 1 }}
/>
<BBFullscreenPlayer
media={media}
visible={media !== null}
onClose={() => setMedia(null)}
/>
</SafeAreaView>
);
}
Key points for tab integration
useFocusEffect— Register the back handler only when the tab is focused. Otherwise, back presses on other tabs would be intercepted.headerShown: false— Hide the default React Navigation header if you want the channel to fill the tab area.- Pause on tab switch — Optionally pause playback when the user switches tabs:
useFocusEffect(
React.useCallback(() => {
// Tab focused — resume if needed
return () => {
// Tab unfocused — pause playback
channelRef.current?.pause();
};
}, [])
);
Back Handling Coordination
When embedding the channel in a navigation stack, coordinate back behavior between the channel and your navigator:
function VideoDetailScreen({ navigation }) {
const channelRef = useRef<BBChannelRef>(null);
const canGoBackRef = useRef(false);
useFocusEffect(
React.useCallback(() => {
const handler = BackHandler.addEventListener('hardwareBackPress', () => {
// Channel navigation first
if (canGoBackRef.current) {
channelRef.current?.goBack();
return true;
}
// Then let React Navigation pop the screen
return false;
});
return () => handler.remove();
}, [])
);
return (
<BBChannel
ref={channelRef}
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
safeAreaEdges={[]}
onCanGoBackChange={(val) => {
canGoBackRef.current = val;
}}
onMediaRequest={(mediaInfo) => {/* ... */}}
style={{ flex: 1 }}
/>
);
}
See Back Navigation for the full guide on back handling patterns.