The Future of Video in React Native: Moving from expo-av to expo-video
Wiktor Gut•Feb 27, 2026•5 min readThe release of Expo SDK 55 marks a turning point for media handling in React Native. Following its previous deprecation, expo-av has now been fully removed, with its functionality officially split into two specialized, modern packages: expo-video and expo-audio.
Having recently led the migration to expo-video at Expensify (as part of the Software Mansion team), we’ll walk you through the key differences between the libraries and share what we learned along the way, so you can migrate with confidence.
Decoupling logic from UI
The most important architectural change is the separation of concerns. We are moving from the monolithic <Video/> component to a dual-system: VideoPlayer for logic control and <VideoView/> for the UI.
This split closely mirrors native media handling, where playback logic and visual rendering have always been distinct entities. By aligning with Android’s ExoPlayer/PlayerView and iOS’s AVPlayer/AVPlayerViewController, expo-video gives developers the same granular control and architectural clarity found in native development.
expo-av: initialization
const videoRef = useRef<Video>(null);
<Video
ref={videoRef}
source={{ uri: VIDEO_URL }}
/>
expo-video: initialization
const player: VideoPlayer = useVideoPlayer(VIDEO_URL);
<VideoView player={player} />
Plus, you can now define the player’s initial state directly during initialization — not just via props on the View, which is much more intuitive:
const player = useVideoPlayer(VIDEO_URL, (player) => {
player.loop = true;
player.muted = true;
player.playbackRate = 1.75;
});
VideoPlayer vs. VideoView: method mapping
Since playback logic has moved from the Video ref to the VideoPlayer object, most methods (play/pause, volume/mute, playback rate, etc.) are now accessed directly through the player. Only a few presentation-related methods remain on the VideoView. This ensures that the native playback logic and the visual presentation layer remain decoupled.
expo-av: monolithic methods
videoRef.current.playAsync();
videoRef.current.pauseAsync();
videoRef.current.setRateAsync(newRate, true);
videoRef.current.setIsMutedAsync(!isMuted);
videoRef.current?.presentFullscreenPlayer();
videoRef.current?.dismissFullscreenPlayer();
expo-video: player vs. view methods
// Playback logic (Player)
player.play();
player.pause();
player.playbackRate = newRate;
player.muted = !muted;
// Presentation logic (View)
videoViewRef.current?.enterFullscreen();
videoViewRef.current?.exitFullscreen();
Synchronous vs. asynchronous calls
Another game-changing update in expo-video is the introduction of synchronous function calls. This preserves the execution order of your code without the need for async/await boilerplate.
expo-av: toggle playback (async)
const togglePlayback = useCallback(async () => {
if (!videoRef.current) return;
if (isPlaying) {
await videoRef.current.pauseAsync();
} else {
await videoRef.current.playAsync();
}
}, [isPlaying]);
expo-video: toggle playback (sync)
const togglePlayback = useCallback(() => {
player.playing ? player.pause() : player.play();
}, [player]);
Modern state management
Finally, expo-video addresses one of the biggest pain points of expo-av: state updates via the onPlaybackStatusUpdate callback. In the old API, this single, massive function was triggered by every state change (including time updates), forcing developers to parse a huge AVPlaybackStatus object and trigger multiple setState calls.
expo-video solves this with a subscription-based approach, splitting updates into atomic, manageable events.
useEvent
Use this hook when you need a value (like isPlaying) that stays in sync with a specific event (playingChange). You can also provide a default value:
const { isPlaying } = useEvent(player, "playingChange", {
isPlaying: player.playing,
});
useEventListener
You can use this hook to react to events immediately. For optimal performance, you should avoid using useEffect for event-driven logic and instead handle reactions directly within this listener:
useEventListener(player, "playToEnd", () => {
console.log("Video finished playing");
});
Performance impact
In expo-av, a typical status update handler looked like this:
const handlePlaybackStatusUpdate = useCallback((status: AVPlaybackStatus) => {
if (status.isLoaded) {
setIsPlaying(status.isPlaying);
setPosition(status.positionMillis);
if(status.rate > 2){
setRateText("It's really fast");
}
}
}, []);
In expo-video, we achieve the same result with significantly better performance and less boilerplate:
const { isPlaying } = useEvent(player, "playingChange", {
isPlaying: player.playing,
});
const { currentTime } = useEvent(player, 'timeUpdate', {
currentTime: 0
});
useEventListener(player, "playbackRateChange", (payload) => {
if(payload.playbackRate > 2){
setRateText("It's really fast");
}
});
Why is this better? In this model, isPlaying only updates when the playingChange event occurs, and the playbackRateChange logic only executes when the rate actually changes. In expo-av, both would be re-evaluated every time currentTime is updated (which happens multiple times per second), leading to unnecessary overhead.
Just the tip of the iceberg
If you want to see these differences in action, you can check out this Expo Snack demo with our side-by-side comparison.
The differences highlighted above are just the beginning. expo-video offers a wealth of new possibilities: from custom buffering and reusing the same VideoPlayer across different screens to native thumbnail generation and robust Picture-in-Picture support. React Native and Expo developers now have more power than ever to tailor the video experience to their needs.
You can explore the full potential through expo-video docs at docs.expo.dev/versions/latest/sdk/video
Need support with your migration?
Migrating from expo-av to expo-video in large-scale projects is more than just a syntax swap — it gives you a chance to refresh your workflow and boost performance.
If your team has encountered a unique edge case, needs a performance audit, or simply wants professional help navigating this transition, reach out to us at Software Mansion. As creators of some of the key tools within the React Native ecosystem (such as Reanimated or Gesture Handler), we’re ready to help you make the most of expo-video and the new SDK 55.
We’re Software Mansion: multimedia experts, AI explorers, React Native core contributors, community builders, and software development consultants.















