This is an Expo Router project that demonstrates choosing images and videos from the device, then uploading them to a server.
Expo uses the web APIs fetch and FormData APIs to upload media to an API route.
Getting the media to an API route as soon as possible is recommended as most services and tools have good server support, and it reduces the chance of bugs from device-specific quirks.
The FormData
approach is optimal as it avoids slow base64 string conversions and memory issues on low-end devices. It works by referencing a local file URL, which is then streamed to the server. From the server, you can access the raw bytes of the file.
- Pick an image or video with
ImagePicker
. - Upload the media with built-in
fetch
andFormData
APIs. - Use Expo Router API routes to handle the upload on the server. This can be deployed to hosting providers by following the API routes deployment guide.
- Save the file using a pseudo-database with Node.js
fs
andpath
modules. In production, you'd use a real database like MongoDB or PostgreSQL.
-
Install dependencies
bun install
-
Start the app
npx expo start
You can run the app on iOS, Android, and the web:
- development build
- Android emulator
- iOS simulator
- Expo Go, a limited sandbox for trying out app development with Expo
You can start developing by editing the files inside the app directory. This project uses file-based routing.
Uploading a local file to a server on native platforms:
import { File } from "expo-file-system";
const result = await ImagePicker.launchImageLibraryAsync();
const formData = new FormData();
formData.append(`photo`, new File(result.assets[0].uri));
const response = await fetch("/api/img", {
method: "POST",
body: formData,
headers: { Accept: "application/json" },
});
Accessing the uploaded file on the server as a File
object, ArrayBuffer
, and Buffer
:
export async function POST(req: Request) {
const formData = await req.formData();
const file = formData.get("photo") as File;
const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
return Response.json({ file, arrayBuffer, buffer });
}
Uploading File
requires using fetch
from expo/fetch
as the React Native fetch
does not support it. The polyfill is included in utils/fetch-polyfill.ts
.
React Native has very limited support for web APIs. We're working on building replacement APIs such as fetch and FormData but these are currently opt-in.
- Enable relative fetch requests: Add the
EXPO_UNSTABLE_DEPLOY_SERVER=1
environment variable to your development environment. This enables relative fetch requests likefetch('/api/hello')
. When you create production build, the server is deployed and linked to the fetch API automatically. Alternatively, you can use absolute URLs likefetch('https://mydomain.com/api/hello')
for each request, just be sure to switch the URLs between production and development. - Use
expo/fetch
polyfill: React Native's fetch does not supportFile
objects or streaming, it also uses base64 strings which are much slower for files as you must convert back and forth from binary data to strings. To useFile
objects, you must use thefetch
polyfill fromexpo/fetch
. This is included inutils/fetch-polyfill.ts
. You can import this file at the top of your app to replace the global fetch with the polyfill. - Use
File
fromexpo-file-system
: Use theFile
class fromexpo-file-system
to createFile
objects from local URIs.
To learn more about developing your project with Expo, look at the following resources:
- Expo documentation: Learn fundamentals, or go into advanced topics with our guides.
- Learn Expo tutorial: Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
Join our community of developers creating universal apps.
- Expo on GitHub: View our open source platform and contribute.
- Discord community: Chat with Expo users and ask questions.