Upload videos from a React Native app
Add resumable, chunked video uploads to a React Native (Expo) app using the FastPix React Native Uploads SDK and a signed upload URL from your backend.
Uploading video from a phone is different from uploading other files. Connections drop, apps get backgrounded, and networks switch between Wi-Fi and cellular mid-transfer. A plain fetch of a whole file starts over every time that happens.
The FastPix React Native Uploads SDK uploads in chunks and resumes from the last confirmed byte, so an interrupted upload continues instead of restarting. Uploads go directly from the device to FastPix, so the video bytes never pass through your server.
This guide continues from the React Native quickstart app. By the end you can pick a video, upload it with progress and pause and resume, and play it back once FastPix has processed it.
WARNING:
The Uploads SDK depends on native modules, so uploading does not run in Expo Go. You get a “native module that doesn’t exist” error. Run a development build (npx expo run:iosornpx expo run:android, or EAS Build) to try it on a device. Playback (the quickstart) still works in Expo Go, and only the upload screen needs the development build.
How it works
- Your backend creates a signed upload URL using the FastPix Node SDK and your Secret Key.
- The app lets the user pick a video with
expo-image-picker. - The React Native Uploads SDK uploads the file to that URL in chunks, emitting progress events.
- FastPix processes the upload asynchronously and assigns it a playback ID.
- Once ready, you play it with the
VideoPlayerfrom the quickstart.
What you’ll build
- A screen with a “Pick a video” button.
- A progress bar with working Pause and Resume controls.
- Playback of the uploaded video once it’s ready.
Before you begin
Make sure you have the following:
- The quickstart app, or any Expo app with a
VideoPlayercomponent. - A FastPix account with an Access Token ID and Secret Key. See get your Access Token ID and Secret Key.
- A backend you control (this guide uses a small Express server) to create signed upload URLs.
- A development build to run the upload screen. The Uploads SDK uses native modules that don’t run in Expo Go.
WARNING:
Your Secret Key must never ship inside the app. Anyone can extract strings from a mobile binary. Create signed upload URLs on a server and have the app request them.
Each code block is a complete file. The bold filename above it tells you where the code goes. Create or replace that file with the code shown.
Create a signed upload URL (backend)
Signed upload URLs are created with your Secret Key, so this belongs on the server. Here’s a minimal Express route:
server.js
Install the server dependencies and set your credentials:
NOTE:
Unlike a browser upload, a native device upload does not need theX-Client-Type: web-browserheader. The signed URL from the call above is ready for the React Native SDK to upload to directly.
Keep the uploadId. It identifies the media in the rest of the API, and you use it to fetch the playback ID once processing finishes.
Install the app dependencies
In your Expo app:
expo-image-picker opens the system library so the user can choose a video. The Uploads SDK handles the chunked, resumable transfer.
NOTE:
If you use a bare React Native project (not managed Expo), runcd ios && pod installafter installing so the native modules link. Managed Expo development builds handle this during prebuild.
Pick a video
Start with a component that opens the picker and shows the chosen file. Point CREATE_UPLOAD_ENDPOINT at your backend route:
src/components/upload-video.tsx
Upload with progress, pause, and resume
Now wire the picked file into FastPixUpload. Three things to know about the API:
endpointtakes an async function, not a URL. The SDK calls it to mint a fresh signed URL right when the upload begins, so URLs aren’t created for videos the user never uploads.chunkSizeis in KB and must be a multiple of 256 KB.5 * 1024is a 5 MB chunk (20 x 256 KB), a good default for mobile. Smaller chunks resume faster on flaky networks but add per-chunk overhead. Keep any custom size 256 KB-aligned, or a mid-upload chunk fails withHTTP 400.- The upload emits events. Subscribe with
upload.on(...)to drive a progress bar, and keep arefto it so buttons canpause()andresume().
This complete component replaces the placeholder from the previous step:
src/components/upload-video.tsx
NOTE:
The SDK also resumes on its own when the network drops and comes back, and when the device switches between Wi-Fi and cellular.pause()andresume()are for user-driven control on top of that. Other events you can subscribe to includestateChange,pause, andresume.
Play the uploaded video
After success, FastPix processes the video before it can be streamed. Once it’s ready, fetch the playback ID for your uploadId from your backend and hand it to the quickstart player:
src/app/watch.tsx
NOTE:
Processing is asynchronous, so the playback ID isn’t available the instant the upload finishes. The reliable signal is thevideo.media.readywebhook to your backend, and you can also poll get media byuploadId.
Troubleshooting
The upload fails immediately with a URL or credentials error.
Your backend route isn’t returning a valid signed URL. Test it directly with curl -X POST https://your-backend.example.com/uploads. It should return JSON with an uploadUrl. Check that FASTPIX_USERNAME and FASTPIX_PASSWORD are set on the server.
The picker returns a URI that the SDK can’t read.
expo-image-picker returns a file:// URI. Pass result.assets[0].uri straight through as fileUri, and don’t strip the scheme. On Android, make sure the app has media permission (the picker requests it on first use).
The chunk size is rejected, or a mid-upload chunk fails with HTTP 400.
chunkSize is in KB, and every chunk except the last must be a multiple of 256 KB. This is a hard requirement of the underlying resumable-upload storage, not FastPix-specific. 5 * 1024 (5 MB, which is 20 x 256 KB) is a safe default and what this guide uses. If you set a custom size, keep it 256 KB-aligned (n * 256). A non-aligned value uploads the first chunk fine but fails a later one with a 400. Also stay within the valid range (roughly 5 MB to 500 MB per chunk).
Every chunk fails with HTTP 400.
Your backend is sending the X-Client-Type: web-browser header when it creates the upload URL. That header is for browser uploaders (which perform a resumable-session handshake). The React Native uploader PUTs chunks directly, so it needs the plain device upload URL you get by omitting that header. Remove it from the backend call and the chunks upload correctly.
Nothing happens after pod install on a bare project.
Rebuild the native app (npx expo run:ios, or open Xcode and run). Installing pods alone doesn’t rebuild an already-installed app.
Frequently asked questions
Can I upload from the app without a backend server?
No. Uploads are authorized by a signed upload URL created with your Secret Key, and the Secret Key must never ship inside a mobile app, since anyone can extract strings from the binary. Create the signed URL on a server you control (this guide uses a small Express route) and have the app request it.
Does uploading work in Expo Go?
No. The Uploads SDK depends on native modules that aren’t in Expo Go, so the upload screen throws a “native module that doesn’t exist” error there. Run a development build with npx expo run:ios or npx expo run:android (or EAS Build). Playback from the quickstart still works in Expo Go; only uploading needs the development build.
How do I know when an uploaded video is ready to play?
Processing is asynchronous, so the playback ID isn’t ready the instant the upload finishes. Listen for the video.media.ready webhook on your backend, or poll get media by uploadId until its status is ready, then use the playback ID with the player.
What’s next
- Play a FastPix video in React Native and Advanced playback in React Native.
- React Native Uploads SDK and Node SDK.
- Set up webhooks and Get media to know when processing finishes.