---
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-53/packages/expo-video
packageName: expo-video
iconUrl: /static/images/packages/expo-video.png
platforms: ["android", "ios", "web", "tvos"]
searchRank: 10
---
`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).
### Caching videos
If your app frequently replays the same video, caching can be utilized to minimize network usage and enhance user experience, albeit at the cost of increased device storage usage. `expo-video` supports video caching on `Android` and `iOS` platforms. This feature can be activated by setting the [`useCaching`](#videosource) property of a [`VideoSource`](#videosource) object to `true`.
The cache is persistent and will be cleared on a least-recently-used basis once the preferred size is exceeded. Furthermore, the system can clear the cache due to low storage availability, so it's not advisable to depend on the cache to store critical data.
The cache functions offline. If a portion or the entirety of a video is cached, it can be played from the cache even when the device is offline until the cached data is exhausted.
> Due to platform limitations, the cache cannot be used with HLS video sources on iOS. Caching DRM-protected videos is not supported on Android and iOS.
### Managing the cache
- The preferred cache size in bytes can be defined using the [`setVideoCacheSizeAsync`](#videosetvideocachesizeasyncsizebytes) function. The default cache size is 1GB.
- The [`getCurrentVideoCacheSize`](#videogetcurrentvideocachesize) can be used to get the current storage occupied by the cache in bytes.
- All cached videos can be cleared using the [`clearVideoCacheAsync`](#videoclearvideocacheasync) function.
## API
```js
```