Skip to main content

Video Player

useVideoPlayer plays a single video file and exposes its current frame as a Reanimated shared value. Each frame is a GPU texture — wrap it with Skia.Image.MakeImageFromNativeTextureUnstable and draw it like any other SkImage.

import { Canvas, Image, Skia } from '@shopify/react-native-skia';
import { useVideoPlayer } from '@azzapp/react-native-skia-video';
import { useDerivedValue } from 'react-native-reanimated';

const MyVideoPlayer = ({ uri, width, height }) => {
const { currentFrame, player } = useVideoPlayer({
uri,
autoPlay: true,
isLooping: true,
onReadyToPlay: ({ width, height, rotation }) =>
console.log(`video is ${width}x${height}, rotated ${rotation}°`),
});

const videoImage = useDerivedValue(() => {
const frame = currentFrame.value;
if (!frame) return null;
return Skia.Image.MakeImageFromNativeTextureUnstable(
frame.texture,
frame.width,
frame.height
);
});

return (
<Canvas style={{ width, height }}>
<Image image={videoImage} x={0} y={0} width={width} height={height} fit="cover" />
</Canvas>
);
};

Options

OptionTypeDescription
uristring | nullThe video to play. Pass null to skip creating the player.
resolution{ width, height }Decode at a lower resolution — a big performance win when the display size is small. Changing it re-creates the player.
autoPlaybooleanStart playback as soon as the video is ready.
isLoopingbooleanRestart from the beginning when playback completes.
volumenumberVolume of the video's audio track (0–1).
playbackSpeednumber1 is normal speed, 2 double, 0.5 half.

Callbacks

CallbackFired when
onReadyToPlay(dimensions)The video is ready; receives { width, height, rotation }.
onBufferingStart() / onBufferingEnd()Network/disk buffering starts or ends.
onBufferingUpdate(ranges)Buffered ranges change ({ start, duration }[]).
onComplete()Playback reaches the end.
onSeekComplete()A seekTo finished.
onPlayingStatusChange(playing)Play/pause state changes.
onError(error, retry)Something failed; call retry() to re-create the player.

Controlling playback

The hook also returns a player controller:

const { currentFrame, player } = useVideoPlayer({ uri });

player?.play();
player?.pause();
player?.seekTo(12.5); // seconds
player?.currentTime; // seconds
player?.duration; // seconds
player?.isPlaying;
Frame lifecycle

The frame textures are owned and recycled by the player. The SkImage wrappers you create around them are cheap; create them per frame in a derived value (as above) or in a frame callback.

For playing several videos on a shared clock — cuts, transitions, overlays — use a Video Composition instead of multiple players.