---
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
```