Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Official Expo team skills for building, deploying, and debugging Expo apps — fine-tuned for Claude Code.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/expo-av-to-video.md
1# Migrating from expo-av to expo-video23## Imports45```tsx6// Before7import { Video, ResizeMode } from 'expo-av';89// After10import { useVideoPlayer, VideoView, VideoSource } from 'expo-video';11import { useEvent, useEventListener } from 'expo';12```1314## Video Playback1516### Before (expo-av)1718```tsx19const videoRef = useRef<Video>(null);20const [status, setStatus] = useState({});2122<Video23ref={videoRef}24source={{ uri: 'https://example.com/video.mp4' }}25style={{ width: 350, height: 200 }}26resizeMode={ResizeMode.CONTAIN}27isLooping28onPlaybackStatusUpdate={setStatus}29/>3031// Control32videoRef.current?.playAsync();33videoRef.current?.pauseAsync();34```3536### After (expo-video)3738```tsx39const player = useVideoPlayer('https://example.com/video.mp4', player => {40player.loop = true;41});4243const { isPlaying } = useEvent(player, 'playingChange', { isPlaying: player.playing });4445<VideoView46player={player}47style={{ width: 350, height: 200 }}48contentFit="contain"49/>5051// Control52player.play();53player.pause();54```5556## Status Updates5758### Before (expo-av)5960```tsx61<Video62onPlaybackStatusUpdate={status => {63if (status.isLoaded) {64console.log(status.positionMillis, status.durationMillis, status.isPlaying);65if (status.didJustFinish) console.log('finished');66}67}}68/>69```7071### After (expo-video)7273```tsx74// Reactive state75const { isPlaying } = useEvent(player, 'playingChange', { isPlaying: player.playing });7677// Side effects78useEventListener(player, 'playToEnd', () => console.log('finished'));7980// Direct access81console.log(player.currentTime, player.duration, player.playing);82```8384## Local Files8586### Before (expo-av)8788```tsx89<Video source={require('./video.mp4')} />90```9192### After (expo-video)9394```tsx95const player = useVideoPlayer({ assetId: require('./video.mp4') });96```9798## Fullscreen and PiP99100```tsx101<VideoView102player={player}103allowsFullscreen104allowsPictureInPicture105onFullscreenEnter={() => {}}106onFullscreenExit={() => {}}107/>108```109110For PiP and background playback, add to app.json:111112```json113{114"expo": {115"plugins": [116["expo-video", { "supportsBackgroundPlayback": true, "supportsPictureInPicture": true }]117]118}119}120```121122## API Mapping123124| expo-av | expo-video |125|---------|------------|126| `<Video>` | `<VideoView>` |127| `ref={videoRef}` | `player={useVideoPlayer()}` |128| `source={{ uri }}` | Pass to `useVideoPlayer(uri)` |129| `resizeMode={ResizeMode.CONTAIN}` | `contentFit="contain"` |130| `isLooping` | `player.loop = true` |131| `shouldPlay` | `player.play()` in setup |132| `isMuted` | `player.muted = true` |133| `useNativeControls` | `nativeControls={true}` |134| `onPlaybackStatusUpdate` | `useEvent` / `useEventListener` |135| `videoRef.current.playAsync()` | `player.play()` |136| `videoRef.current.pauseAsync()` | `player.pause()` |137| `videoRef.current.replayAsync()` | `player.replay()` |138| `videoRef.current.setPositionAsync(ms)` | `player.currentTime = seconds` |139| `status.positionMillis` | `player.currentTime` (seconds) |140| `status.durationMillis` | `player.duration` (seconds) |141| `status.didJustFinish` | `useEventListener(player, 'playToEnd')` |142143## Key Differences144145- **Separate player and view**: Player logic decoupled from the view—one player can be used across multiple views146- **Time in seconds**: Uses seconds, not milliseconds147- **Event system**: Uses `useEvent`/`useEventListener` from `expo` instead of callback props148- **Video preloading**: Create a player without mounting a VideoView to preload for faster transitions149- **Built-in caching**: Set `useCaching: true` in VideoSource for persistent offline caching150151## Known Issues152153- **Uninstall expo-av first**: On Android, having both expo-av and expo-video installed can cause VideoView to show a black screen. Uninstall expo-av before installing expo-video154- **Android: Reusing players**: Mounting the same player in multiple VideoViews simultaneously can cause black screens on Android (works on iOS)155- **Android: currentTime in setup**: Setting `player.currentTime` in the `useVideoPlayer` setup callback may not work on Android—set it after mount instead156- **Changing source**: Use `player.replace(newSource)` to change videos without recreating the player157158## API Reference159160https://docs.expo.dev/versions/latest/sdk/video/161