A TikTok-style vertical video feed built with React Native and Expo, featuring buttery-smooth scrolling powered by Shopify's FlashList.
- React Native (0.81) with Expo (SDK 54)
- @shopify/flash-list - High-performance list component
- expo-video - Native video playback
- Mux - Video streaming infrastructure
- react-native-gesture-handler - Tap gestures for play/pause and double-tap to like
- react-native-reanimated - Smooth animations
Traditional FlatList struggles with video feeds because it creates and destroys video components aggressively during scroll, leading to:
- Janky scrolling
- Delayed video loading
- Memory spikes from multiple video instances
FlashList uses cell recycling (similar to Android's RecyclerView and iOS's UICollectionView) which dramatically improves performance:
| Metric | FlatList | FlashList |
|---|---|---|
| JS Thread FPS | ~45-50 | ~60 (stable) |
| Blank cells during fast scroll | Frequent | Rare |
| Memory usage | Higher (no recycling) | Lower (cell reuse) |
<FlashList
data={videos}
renderItem={renderItem}
// Pre-calculate item sizes for instant layout
overrideItemLayout={(layout, _item, index) => {
layout.size = SCREEN_HEIGHT;
layout.offset = SCREEN_HEIGHT * index;
}}
// Render items within 3 screens for smooth scroll
drawDistance={SCREEN_HEIGHT * 3}
// Single item type = optimal recycling pool
getItemType={() => "video"}
// Full-screen snapping
pagingEnabled
snapToInterval={SCREEN_HEIGHT}
decelerationRate="fast"
/>The feed implements directional preloading to minimize buffering:
const MAX_PRELOAD_DISTANCE = 5;
// Preload 5 videos ahead in scroll direction
const shouldPreloadAhead = isAhead && Math.abs(distanceFromActive) <= MAX_PRELOAD_DISTANCE;
// Keep 1 video behind ready for quick back-scroll
const shouldPreloadBehind = !isAhead && Math.abs(distanceFromActive) === 1;Videos not in the preload window have their source set to null, freeing up memory while maintaining the recycled view structure.
# Install dependencies
bun install
# Start the dev server
npx expo start
# Run on iOS
npx expo run:ios
# Run on Android
npx expo run:android├── app/ # Expo Router pages
│ ├── (tabs)/ # Tab navigation
│ │ ├── index.tsx # Home feed
│ │ ├── discover.tsx # Discover page
│ │ ├── create.tsx # Create video
│ │ ├── inbox.tsx # Notifications
│ │ └── profile.tsx # User profile
├── components/
│ └── video-feed/
│ ├── video-feed.tsx # FlashList implementation
│ ├── video-item.tsx # Individual video player
│ └── video-overlay.tsx # UI overlay (likes, comments)
├── hooks/
│ ├── use-videos.ts # Video data fetching
│ ├── use-likes.ts # Like state management
│ └── use-video-playback.ts # Play/pause logic
└── types/
└── video.ts # TypeScript definitions
