Skip to main content

Deep Linking

Navigate users directly to specific content — a video, playlist, search results, or sub-channel.

All channel navigation is represented by BBNavigationState:

interface BBNavigationState {
pageType: 'main' | 'detailPage' | 'overviewPage' | 'searchPage';
contentId?: string; // Clip or playlist ID
contentType?: string; // 'mediaclip' or 'mediacliplist'
searchQuery?: string; // Active search term
subChannelId?: string; // Active sub-channel in multi-channel setup
}

Initial Navigation State

Open the channel at a specific location by passing initialNavigationState:

// Open directly on a video detail page
<BBChannel
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
initialNavigationState={{
pageType: 'detailPage',
contentId: '12345',
contentType: 'mediaclip',
}}
/>

// Open on a playlist overview
<BBChannel
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
initialNavigationState={{
pageType: 'overviewPage',
contentId: '678',
contentType: 'mediacliplist',
}}
/>

// Open with search results
<BBChannel
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
initialNavigationState={{
pageType: 'searchPage',
searchQuery: 'highlights',
}}
/>

// Open on a specific sub-channel
<BBChannel
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
initialNavigationState={{
pageType: 'main',
subChannelId: 'sports',
}}
/>

State Persistence

Save the navigation state so users return where they left off:

import AsyncStorage from '@react-native-async-storage/async-storage';

const NAV_STATE_KEY = '@channel_navigation';

function ChannelScreen() {
const [initialState, setInitialState] = useState<BBNavigationState | undefined>();
const [isReady, setIsReady] = useState(false);

useEffect(() => {
AsyncStorage.getItem(NAV_STATE_KEY)
.then((json) => {
if (json) setInitialState(JSON.parse(json));
})
.finally(() => setIsReady(true));
}, []);

if (!isReady) return null;

return (
<BBChannel
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
initialNavigationState={initialState}
onNavigationStateChange={(state) => {
AsyncStorage.setItem(NAV_STATE_KEY, JSON.stringify(state));
}}
/>
);
}

Programmatic Navigation

After the channel is loaded, use ref methods to navigate:

const channelRef = useRef<BBChannelRef>(null);

// Navigate to a specific entity
channelRef.current?.navigateToEntity('mediaclip', '12345');
channelRef.current?.navigateToEntity('mediacliplist', '678');

// Set full navigation state
channelRef.current?.setNavigationState({
pageType: 'detailPage',
contentId: '12345',
contentType: 'mediaclip',
});

// Search
channelRef.current?.search('highlights');

// Navigate to a sub-channel
channelRef.current?.navigateToSubChannel('sports');

// Go back to main page
channelRef.current?.navigateTo('main');
warning

Programmatic navigation only works after the channel is ready. Wait for the onReady callback before calling navigation methods.

Web Hash URL Mapping

The web channel stores navigation state in the URL hash as #bbch=<base64>. You can convert between hash URLs and BBNavigationState:

Web URL HashNavigation State
#bbch=eyJwIjoibWFpbiJ9{ pageType: 'main' }
#bbch=eyJwIjoiZCIsImNpIjoiMTIzNDUiLCJjdCI6Im1jIn0={ pageType: 'detailPage', contentId: '12345', contentType: 'mediaclip' }
#bbch=eyJwIjoicyIsInNxIjoiaGlnaGxpZ2h0cyJ9{ pageType: 'searchPage', searchQuery: 'highlights' }

The base64-encoded JSON uses shortened keys:

Short KeyFull Property
ppageType (main, d = detailPage, o = overviewPage, s = searchPage)
cicontentId
ctcontentType (mc = mediaclip, mcl = mediacliplist)
sqsearchQuery
scsubChannelId

Use React Native's Linking API to handle deep links from outside the app:

import { Linking } from 'react-native';

function ChannelScreen() {
const channelRef = useRef<BBChannelRef>(null);
const [initialState, setInitialState] = useState<BBNavigationState | undefined>();
const [isReady, setIsReady] = useState(false);
const channelReady = useRef(false);

// Parse deep link URL into navigation state
function parseDeepLink(url: string): BBNavigationState | undefined {
const parsed = new URL(url);
const path = parsed.pathname; // e.g., /video/12345 or /search/query

if (path.startsWith('/video/')) {
return {
pageType: 'detailPage',
contentId: path.split('/')[2],
contentType: 'mediaclip',
};
}
if (path.startsWith('/playlist/')) {
return {
pageType: 'overviewPage',
contentId: path.split('/')[2],
contentType: 'mediacliplist',
};
}
if (path.startsWith('/search/')) {
return {
pageType: 'searchPage',
searchQuery: decodeURIComponent(path.split('/')[2]),
};
}
return undefined;
}

// Handle initial deep link (cold start)
useEffect(() => {
Linking.getInitialURL().then((url) => {
if (url) setInitialState(parseDeepLink(url));
setIsReady(true);
});
}, []);

// Handle deep links while app is running (warm start)
useEffect(() => {
const subscription = Linking.addEventListener('url', ({ url }) => {
const state = parseDeepLink(url);
if (state && channelReady.current) {
channelRef.current?.setNavigationState(state);
}
});
return () => subscription.remove();
}, []);

if (!isReady) return null;

return (
<BBChannel
ref={channelRef}
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
initialNavigationState={initialState}
onReady={() => {
channelReady.current = true;
}}
/>
);
}

URL Scheme Examples

Define your deep link URL scheme and map routes to navigation states:

URLNavigation State
myapp://channel{ pageType: 'main' }
myapp://channel/video/12345{ pageType: 'detailPage', contentId: '12345', contentType: 'mediaclip' }
myapp://channel/playlist/678{ pageType: 'overviewPage', contentId: '678', contentType: 'mediacliplist' }
myapp://channel/search/highlights{ pageType: 'searchPage', searchQuery: 'highlights' }
myapp://channel/tab/sports{ pageType: 'main', subChannelId: 'sports' }
https://example.com/videos/12345{ pageType: 'detailPage', contentId: '12345', contentType: 'mediaclip' }

Configure these in your app's AndroidManifest.xml (Android) and Info.plist (iOS) as intent filters and universal links respectively.