This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
Expo Video (expo-video)
A library that provides an API to implement video playback in apps.
expo-video is a cross-platform, performant video component for React Native and Expo with Web support.
Known issues Android
When two VideoView components are overlapping and have the contentFit prop set to cover, one of the videos may be displayed out of bounds. This is a known upstream issue. To work around this issue, use the surfaceType prop and set it to textureView.
Installation
If you are installing this in an existing React Native app, make sure to install expo in your project.
Configuration in app config
You can configure expo-video using its built-in config plugin if you use config plugins in your project (Continuous Native Generation (CNG)). 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 CNG, then you'll need to manually configure the library.
Example app.json with config plugin
Configurable properties
Usage
Here's a simple example of a video with a play and pause button.
Receiving events
The changes in properties of the 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 it emits.
The event system is based on the EventEmitter class and hooks from the 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.
useEventListener hook
Built around the Player.addListener and Player.removeListener methods, creates an event listener with automatic cleanup.
Player.addListener method
Most flexible way to listen to events, but requires manual cleanup and more boilerplate code.
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 if you also want to configure other properties.
Playing media from the media library
expo-video supports playing videos picked from user's media library using expo-media-library/legacy or any valid PHAsset URI with appropriate permissions.
To play a video from the media library, you should obtain an Asset object with MediaLibrary.getAssetsAsync() and use its uri property as the uri of the video source.
Before playing, make sure to request the necessary permissions using MediaLibrary.requestPermissionsAsync().
On iOS make sure not to use the localUri property of the asset info, as it does not contain the necessary permissions to read the asset.
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:
Using the VideoPlayer directly
In most cases, the useVideoPlayer 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 function. You need be aware of the risks that come with this approach, as it is your responsibility to call the release() method when the player is no longer needed. If not handled properly, this approach may lead to memory leaks.
On Android, mounting multiple
VideoViewcomponents at the same time with the sameVideoPlayerinstance will not work due to a platform limitation.
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 property of a 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
setVideoCacheSizeAsyncfunction. The default cache size is 1GB. - The
getCurrentVideoCacheSizecan be used to get the current storage occupied by the cache in bytes. - All cached videos can be cleared using the
clearVideoCacheAsyncfunction.
Intercepting native asset loading
This is a native expo-video feature meant for advanced users. Expand to learn more.
Introduction
expo-video includes a native extension point on iOS called VideoAssetTransportProvider. It allows you to intercept specific video sources, customize how the underlying AVURLAsset is created, and override how it loads data.
This is useful when a source cannot be handled by AVKit directly and needs native preprocessing. For example, a provider can rewrite a URL, attach a custom AVAssetResourceLoaderDelegate, start a local proxy server, or transform one streaming format into another before playback begins.
Providers are registered in the VideoAssetTransportRegistry at module startup. When expo-video loads a source, it creates a VideoAssetSourceDescriptor and asks registered providers, in priority order, whether they want to handle it. The first provider that returns a VideoAssetLoadPlan is used.
This API is intended for advanced native integrations. It requires a custom native module, so it is not available in Expo Go. Use a development build instead.
Customizable properties
The main customization points are the fields on VideoAssetTransportProvider and the fields on the VideoAssetLoadPlan it returns.
VideoAssetTransportProvider lets you control the following properties:
VideoAssetLoadPlan lets you control how expo-video constructs and manages the asset via the following properties:
Implementation and usage
A typical setup looks as follows:
- Create an Expo module with
create-expo-module. - Add a class that conforms to
VideoAssetTransportProvider. - Register the provider in the module's
OnCreateblock. - Build the app so the native module is compiled into your project.
If you only need the provider in a single app, use the local Expo module flow.
If you want to reuse the provider across multiple apps, use the standalone Expo module flow instead.
Basic usage example
- Define the provider and load plan. After creating the module, add a class that conforms to
VideoAssetTransportProvider. InmakeLoadPlan(for:), inspect theVideoAssetSourceDescriptorand returnnilfor sources you do not want to handle.
import ExpoVideo final class ExampleVideoTransportProvider: VideoAssetTransportProvider { static let providerIdentifier = "com.example.video-transport" let identifier = Self.providerIdentifier let priority = 500 func makeLoadPlan(for source: VideoAssetSourceDescriptor) -> VideoAssetLoadPlan? { guard source.contentTypeHint == .dash, source.url.pathExtension == "mpd" else { return nil } let transformedURL = URL(string: "http://127.0.0.1:8080/master.m3u8")! return VideoAssetLoadPlan( assetURL: transformedURL, reportedContentTypeHint: .hls ) } }
- To register the provider, the recommended place to register a provider is an Expo module's
OnCreateblock. This ensures the provider is available before videos start loading.
import ExpoModulesCore import ExpoVideo public final class CustomVideoTransportModule: Module { public func definition() -> ModuleDefinition { Name("CustomVideoTransport") OnCreate { VideoAssetTransportRegistry.registerProvider(ExampleVideoTransportProvider()) } OnDestroy { VideoAssetTransportRegistry.unregisterProvider( withId: ExampleVideoTransportProvider.providerIdentifier ) } } }
Complete examples
You can use the following complete examples for a better grasp of how to use this API.
- Basic DASH support provider - a local module in Bare Expo that provides limited DASH support for
expo-videoon iOS. expo-videoiOS caching - the built-in cache provider implementation.
API
import { VideoView, useVideoPlayer } from 'expo-video';
No API data file found, sorry!