Skip to main content
Glama
video.md8.19 kB
--- title: Video (expo-video) description: A library that provides an API to implement video playback in apps. sourceCodeUrl: https://github.com/expo/expo/tree/sdk-52/packages/expo-video packageName: expo-video iconUrl: /static/images/packages/expo-video.png platforms: ["android", "ios", "web", "tvos"] isNew: true --- `expo-video` is a cross-platform, performant video component for React Native and Expo with Web support. ## Installation ## Configuration in app config You can configure `expo-video` using its built-in [config plugin](/config-plugins/introduction/) if you use config plugins in your project ([EAS Build](/build/introduction) or `npx expo run:[android|ios]`). The plugin allows you to configure various properties that cannot be set at runtime and require building a new app binary to take effect. If your app does **not** use EAS Build, then you'll need to manually configure the package. ```json app.json { "expo": { "plugins": [ [ "expo-video", { "supportsBackgroundPlayback": true, "supportsPictureInPicture": true } ] ], } } ``` ## Usage Here's a simple example of a video with a play and pause button. ```jsx const videoSource = 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'; const player = useVideoPlayer(videoSource, player => { player.loop = true; player.play(); }); const { isPlaying } = useEvent(player, 'playingChange', { isPlaying: player.playing }); return ( <View style={styles.contentContainer}> <View style={styles.controlsContainer}> <Button title={isPlaying ? 'Pause' : 'Play'} onPress={() => { if (isPlaying) { player.pause(); } else { player.play(); } }} /> </View> </View> ); } const styles = StyleSheet.create({ contentContainer: { flex: 1, padding: 10, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 50, }, video: { width: 350, height: 275, }, controlsContainer: { padding: 10, }, }); ``` ### Receiving events The changes in properties of the [`VideoPlayer`](#videoplayer) do not update the React state. Therefore, to display the information about the current state of the `VideoPlayer`, it is necessary to listen to the [events](#videoplayerevents) it emits. The event system is based on the [`EventEmitter`](../sdk/expo/#eventemitter) class and [hooks](../sdk/expo/#hooks) from the [`expo`](../sdk/expo) package. There are a few ways to listen to events: #### `useEvent` hook Creates a listener that will return a stateful value that can be used in a component. It also cleans up automatically when the component unmounts. ```tsx useEvent // ... Other imports, definition of the component, creating the player etc. const { status, error } = useEvent(player, 'statusChange', { status: player.status }); // Rest of the component... ``` #### `useEventListener` hook Built around the `Player.addListener` and `Player.removeListener` methods, creates an event listener with automatic cleanup. ```tsx useEventListener // ...Other imports, definition of the component, creating the player etc. useEventListener(player, 'statusChange', ({ status, error }) => { setPlayerStatus(status); setPlayerError(error); console.log('Player status changed: ', status); }); // Rest of the component... ``` #### `Player.addListener` method Most flexible way to listen to events, but requires manual cleanup and more boilerplate code. ```tsx Player.addListener // ...Imports, definition of the component, creating the player etc. useEffect(() => { const subscription = player.addListener('statusChange', ({ status, error }) => { setPlayerStatus(status); setPlayerError(error); console.log('Player status changed: ', status); }); return () => { subscription.remove(); }; }, []); // Rest of the component... ``` ### Playing local media from the assets directory `expo-video` supports playing local media loaded using the `require` function. You can use the result as a source directly, or assign it to the `assetId` parameter of a [`VideoSource`](#videosource) if you also want to configure other properties. ```tsx Playing local media const assetId = require('./assets/bigbuckbunny.mp4'); const videoSource: VideoSource = { assetId, metadata: { title: 'Big Buck Bunny', artist: 'The Open Movie Project', }, }; const player1 = useVideoPlayer(assetId); // You can use the `asset` directly as a video source const player2 = useVideoPlayer(videoSource); ``` ### Preloading videos While another video is playing, a video can be loaded before showing it in the view. This allows for quicker transitions between subsequent videos and a better user experience. To preload a video, you have to create a `VideoPlayer` with a video source. Even when the player is not connected to a `VideoView`, it will fill the buffers. Once it is connected to the `VideoView`, it will be able to start playing without buffering. In some cases, it is beneficial to preload a video later in the screen lifecycle. In that case, a `VideoPlayer` with a `null` source should be created. To start preloading, replace the player source with a video source using the `replace()` function. Here is an example of how to preload a video: ```tsx const bigBuckBunnySource: VideoSource = 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'; const elephantsDreamSource: VideoSource = 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4'; const player1 = useVideoPlayer(bigBuckBunnySource, player => { player.play(); }); const player2 = useVideoPlayer(elephantsDreamSource, player => { player.currentTime = 20; }); const [currentPlayer, setCurrentPlayer] = useState(player1); const replacePlayer = useCallback(async () => { currentPlayer.pause(); if (currentPlayer === player1) { setCurrentPlayer(player2); player1.pause(); player2.play(); } else { setCurrentPlayer(player1); player2.pause(); player1.play(); } }, [player1, currentPlayer]); return ( <View style={styles.contentContainer}> <TouchableOpacity style={styles.button} onPress={replacePlayer}> <Text style={styles.buttonText}>Replace Player</Text> </TouchableOpacity> </View> ); } const styles = StyleSheet.create({ contentContainer: { flex: 1, padding: 10, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 50, }, button: { alignItems: 'center', justifyContent: 'center', borderRadius: 3, paddingVertical: 8, paddingHorizontal: 12, backgroundColor: '#4630ec', }, buttonText: { fontSize: 12, fontWeight: 'bold', color: '#eeeeee', textAlign: 'center', }, video: { width: 300, height: 168.75, marginVertical: 20, }, }); ``` ### Using the VideoPlayer directly In most cases, the [`useVideoPlayer`](#usevideoplayersource-setup) hook should be used to create a `VideoPlayer` instance. It manages the player's lifecycle and ensures that it is properly disposed of when the component is unmounted. However, in some advanced use cases, it might be necessary to create a `VideoPlayer` that does not get automatically destroyed when the component is unmounted. In those cases, the `VideoPlayer` can be created using the [`createVideoPlayer`](#videocreatevideoplayersource) function. You need be aware of the risks that come with this approach, as it is your responsibility to call the [`release()`](../sdk/expo/#release) method when the player is no longer needed. If not handled properly, this approach may lead to memory leaks. ```tsx Creating player instance const player = createVideoPlayer(videoSource); ``` > **warning** On Android, mounting multiple `VideoView` components at the same time with the same `VideoPlayer` instance will not work due to a [platform limitation](https://github.com/expo/expo/issues/35012). ## API ```js ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jaksm/expo-docs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server