Creating youtube shorts component in react-native
The current story shares a brief approach to making apps like Youtube shorts in React Native.
I chose to use react-native-youtube-iframe here since there is an issue with mounting multiple videos in android with react-native-youtube.
Let’s see how to code right away.
1. Wrap component with FlatList
<FlatList
ref={flatListRef}
data={items}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
keyExtractor={item => item?.id}
onEndReached={onEndReached}
renderItem={useCallback(
({item, index}: {item: Item<T>; index: number}) => {
return (
<ReelItem
// TODO
/>
);
},
[layout, visibleIndex],
)}
pagingEnabled
/>
Since FlatList extends the ScrollView, you can use pagingEnabled props to implement paging.
2. Write ShortItem which is a component for playing youtube shorts
type ShortItemProps = {
index: number;
visible: boolean;
playing: boolean;
paused: boolean;
url: string;
layout: LayoutRectangle;
};function ShortItem({visible, playing, url, layout}: ShortItemProps) {
const youtubeId = getYoutubeIdFromURL(url);
const youtubePlayerRef = useRef<YoutubeIframeRef>(null);return (
<YoutubePlayer
ref={youtubePlayerRef}
height={layout.height}
width={layout.width}
videoId={youtubeId}
play={playing}
onChangeState={event => {
if (event === 'ended' && visible) {
youtubePlayerRef?.current?.seekTo(0, true);
}
}}
webViewProps={{
injectedJavaScript: `
var element = document.getElementsByClassName('container')[0];
element.style.position = 'unset';
true;
`,
}}
/>
);
}export default ShortItem;
- In
onChangeState
, play time seeks to 0 and continuously loops. - In
webViewProps
, I injected javascript in order to view the video in fullscreen. You can see the related issue here.
3. Check the focused video to handle which video should play
If you see the shorts like the above link, the shorts is only playing the focused video. To achieve that I use viewabilityConfig and onViewableItemsChanged in FlatList. I used the below values and it worked great.
const viewabilityConfig = {
viewAreaCoveragePercentThreshold: 50,
};
- viewAreaCoveragePercentThreshold: Percent of viewport that must be covered for a partially occluded item to count as “viewable”, 0–100. Fully visible items are always considered viewable. A value of 0 means that a single pixel in the viewport makes the item viewable, and a value of 100 means that an item must be either entirely visible or cover the entire viewport to count as viewable.
const onViewRef = useRef((viewableItems: any) => {
if (viewableItems?.viewableItems?.length > 0) {
const index = viewableItems?.viewableItems?.[0]?.index;
setVisibleIndex(index);
}
});...viewabilityConfig={viewabilityConfig}
- The
visibleIndex
value is saved in react state variable. Therefore we know which item the user is watching.
4. Parse the youtube id from the URL
export const getYoutubeIdFromURL = (url: string): string | undefined => {
if (url.includes('?')) {
const arr = url.split('?');
arr.pop();
url = arr[0];
}
const arr = url.split(/(vi\/|v%3D|v=|\/v\/|youtu\.be\/|\/embed\/)/);
const youtubeId = undefined !== arr[2] ? arr[2].split(/[^\w-]/i)[0] : arr[0]; if (youtubeId.includes('https://youtube.com/shorts/')) {
return youtubeId.replace('https://youtube.com/shorts/', '');
} return youtubeId;
};
The react-native-youtube-iframe need videoId props to play the youtube video. Since the youtube URL is more widely used, I parsed the youtube URL and get extracted youtubeId.
5. Prepare the data
type Item = {
id: string;
url: string;
};const items: Item[] = [
{
id: '001',
url: 'https://youtube.com/shorts/Uj74798gItc',
},
{
id: '002',
url: 'https://youtube.com/shorts/HXyx8Sr5RTQ',
},
{
id: '003',
url: 'https://youtube.com/shorts/QgAA_5IPNIs',
},
{
id: '004',
url: 'https://youtube.com/shorts/GFAa6l5zbHE',
},
{
id: '005',
url: 'https://youtube.com/shorts/-IcYublDy7I',
},
{
id: '006',
url: 'https://youtube.com/shorts/6a1tmHi6d60',
},
{
id: '007',
url: 'https://youtube.com/shorts/8Lt1hJnEcq0',
},
{
id: '008',
url: 'https://youtube.com/shorts/266xNTZN5VI',
},
{
id: '009',
url: 'https://youtube.com/shorts/xZ48_razkME',
},
{
id: '010',
url: 'https://youtube.com/shorts/qoM9tP69USo',
},
];
6. Finally use @dooboo/react-native-youtube-iframe to handle the player.
When the short item is visible or hidden, we need to pause the video or play the video. However, react-native-youtube-iframe did not support this.
Therefore, I created a PR to handle this and I am using my own package until this will get merged.
Final Result.
The full source code is here. Hope my sample example helps some readers 🙌.