> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://fastpix.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://fastpix.com/docs/_mcp/server.

# Play a video in React Native

FastPix delivers video as HLS, an adaptive-bitrate format that adjusts quality to each device and network. In React Native, [`expo-video`](https://docs.expo.dev/versions/latest/sdk/video/) wraps the native players (AVPlayer on iOS, ExoPlayer on Android), both of which play HLS out of the box.

So playback comes down to one thing: pass a FastPix stream URL to `expo-video`. This guide builds a small, reusable player component that does exactly that.

\


## How it works

1. You have a FastPix playback ID from a video uploaded using [Upload a video from a device](/docs/upload-videos/upload-videos-from-device) or [Upload a video from a URL](/docs/upload-videos/upload-videos-from-a-url).
2. You build the HLS URL `https://stream.fastpix.com/{PLAYBACK_ID}.m3u8`.
3. `useVideoPlayer` loads that URL, and `VideoView` renders it with native controls.

\


## What you'll build

A `VideoPlayer` component that takes a playback ID as a prop and plays it full width with native controls, fullscreen, and Picture-in-Picture. You drop it into any screen with `<VideoPlayer playbackId="..." />`.

\


## Before you begin

Make sure you have the following:

* A FastPix **playback ID**. To follow along, use the sample ID `7c8d5087-edf7-462f-a1b3-e2fbd30747fa`, then swap in your own.
* Node.js and the Expo tooling. You don't need native build tools to test in [Expo Go](https://expo.dev/go), but you do need a development build for Picture-in-Picture.
* A device or simulator: the iOS Simulator, an Android emulator, or a physical phone.

Each code block in this guide is a complete file. The bold filename above it tells you where the code goes. Create or replace that file with the code shown, then move on.

\


## Create the app

If you don't already have an app, create one with Expo:

```bash
npx create-expo-app@latest fastpix-video-app
cd fastpix-video-app
```

\


## Install expo-video

```bash
npx expo install expo-video
```

Using `expo install` rather than `npm install` picks the `expo-video` version that matches your Expo SDK.

\


## Build the player

Keep the URL format in one place, so components only ever deal with playback IDs. Create a small helper:

**`src/constants/fastpix.ts`**

```ts
/** HLS stream URL for a FastPix playback ID, ready to hand to expo-video. */
export function getStreamUrl(playbackId: string): string {
  return `https://stream.fastpix.com/${playbackId}.m3u8`;
}
```

Now create the player. It takes the playback ID as a prop, sets up the player once, and renders it:

**`src/components/video-player.tsx`**

```tsx
import { useVideoPlayer, VideoView } from "expo-video";
import { StyleSheet, View } from "react-native";
import { getStreamUrl } from "../constants/fastpix";

type VideoPlayerProps = {
  /** FastPix playback ID for the video you want to play. */
  readonly playbackId: string;
  /** Restart automatically when the video ends. Defaults to false. */
  readonly loop?: boolean;
  /** Start playing as soon as the player is ready. Defaults to true. */
  readonly autoPlay?: boolean;
};

export default function VideoPlayer({
  playbackId,
  loop = false,
  autoPlay = true,
}: VideoPlayerProps) {
  const player = useVideoPlayer(getStreamUrl(playbackId), (player) => {
    player.loop = loop;
    if (autoPlay) player.play();
  });

  return (
    <View style={styles.container}>
      <VideoView
        player={player}
        style={styles.video}
        contentFit="contain"
        allowsPictureInPicture
        nativeControls
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#000",
  },
  video: {
    width: "100%",
    aspectRatio: 16 / 9,
  },
});
```

> **NOTE:** \
> The setup callback passed to `useVideoPlayer` runs once when the player is created. Put initial configuration (`loop`, autoplay, muting) there rather than in the render body, which can run many times.

Render it from a screen. Keep the playback ID at the screen level so the player component stays reusable:

**`src/app/index.tsx`**

```tsx
import VideoPlayer from "../components/video-player";

// Replace with your own playback ID from https://dashboard.fastpix.com
const PLAYBACK_ID = "7c8d5087-edf7-462f-a1b3-e2fbd30747fa";

export default function HomeScreen() {
  return <VideoPlayer playbackId={PLAYBACK_ID} />;
}
```

\


## Run the app

```bash
npx expo start
```

Press `i` for the iOS Simulator or `a` for the Android emulator, or scan the QR code with Expo Go on your phone. The video loads and plays with native controls.

> **NOTE:** \
> On Android, HTTPS streams like FastPix work with no extra setup. If you point the player at a plain `http://` URL during local testing, Android blocks it by default (cleartext traffic). Stick to the `https://stream.fastpix.com` URLs and you won't hit this.

\


## Customize playback

`VideoView` and the player expose the common controls directly, with small edits to the component above:

* **Start muted:** add `player.muted = true;` inside the `useVideoPlayer` setup callback, next to `player.loop = loop;`.
* **Loop by default:** pass the prop when you render the component: `<VideoPlayer playbackId={PLAYBACK_ID} loop />`.
* **Portrait (reels) layout:** in the `video` style at the bottom of the file, change `aspectRatio: 16 / 9` to `aspectRatio: 9 / 16`.

\


## Troubleshooting

**The player area is black and nothing plays.**

Check the playback ID and that the asset is ready. Open `https://stream.fastpix.com/{PLAYBACK_ID}.m3u8` in a browser, or `https://play.fastpix.com/?playbackId={PLAYBACK_ID}`. If that doesn't play, the ID is wrong or the media is still processing, which is not a React Native issue.

**`Unable to resolve "expo-video"`.**

The package isn't installed in this project. Run `npx expo install expo-video`, then restart the bundler with `npx expo start -c` to clear the Metro cache.

**The video plays but there are no controls.**

`nativeControls` is what draws the play, scrub, and fullscreen UI. Make sure it's set on `VideoView` (it is in the component above).

**Picture-in-Picture does nothing in Expo Go.**

Picture-in-Picture needs the native configuration from a development build. Create one with `npx expo run:ios` or `npx expo run:android` (or EAS Build). It doesn't work in Expo Go.

\


#### Frequently asked questions

`expo-video` needs the `expo` package, so a bare React Native project has to add Expo modules first. See Expo's [install Expo modules](https://docs.expo.dev/bare/installing-expo-modules/) guide. Apps created with `create-expo-app` already have everything.

Basic playback works in Expo Go. You only need a development build for advanced features such as Picture-in-Picture and background playback, covered in [advanced playback in React Native](/docs/frameworks/react-native-advanced-playback).

Upload or import a video, then read the playback ID from the media's Overview page in the dashboard or from the API response. See [upload a video from a device](/docs/upload-videos/upload-videos-from-device) or [upload a video from a URL](/docs/upload-videos/upload-videos-from-a-url). To just try the guide, use the sample ID above.

\


## What's next

* [React Native Uploads SDK](https://github.com/FastPix/react-native-uploader) and [Node SDK](https://github.com/FastPix/node-sdk).
* [expo-video reference](https://docs.expo.dev/versions/latest/sdk/video/) for the full player API.