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.