Skip to Content
MobileExpo

Expo Integration

This guide explains how to integrate FileZen with Expo/React Native applications. We recommend using our dedicated SDK packages for the best experience.

📁 Code Example: View the complete working example at github.com/FileZen/filezen/tree/main/apps/expo-app 

Installation

Install the required FileZen packages for an Expo project:

Setup

1. Environment Configuration

Set your FileZen API key as an environment variable. This can be done in your local .env file during development or as a system environment variable in production.

.env
FILEZEN_API_KEY=your_api_key_here

The FileZen SDK automatically detects the FILEZEN_API_KEY environment variable for server-side operations, so no additional configuration is needed in your API routes.

2. Provider Setup

Choose between two providers based on your needs:

Option A: ZenStorageProvider (Direct Upload)

For direct uploads to FileZen without a server:

app/_layout.tsx
import { ZenStorageProvider } from '@filezen/react'; export default function RootLayout() { return ( <ZenStorageProvider apiKey="your-filezen-api-key" // Optional: can use FILEZEN_API_KEY env variable > {/* Your app components */} </ZenStorageProvider> ); }

Option B: ZenClientProvider (Server Integration)

For server-side presigned URL generation:

app/_layout.tsx
import { ZenClientProvider } from '@filezen/react'; export default function RootLayout() { return ( <ZenClientProvider signUrl="https://your-server.com/api/filezen/sign" // Your server endpoint > {/* Your app components */} </ZenClientProvider> ); }

Provider Comparison: ZenStorageProvider is simpler for direct uploads, while ZenClientProvider requires a server but keeps your API key secure on the backend.

Using FileZen in Expo

Basic File Upload Component

Create a component that handles file uploads using Expo’s file pickers:

components/FileUpload.tsx
import { useFileZen } from '@filezen/react'; import * as DocumentPicker from 'expo-document-picker'; import * as ImagePicker from 'expo-image-picker'; import React, { useState } from 'react'; import { Alert, Text, TouchableOpacity, View } from 'react-native'; export default function FileUpload() { const { upload } = useFileZen(); const [uploading, setUploading] = useState(false); const pickAndUploadImage = async () => { try { const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.All, allowsEditing: false, quality: 1, }); if (!result.canceled && result.assets[0]) { const asset = result.assets[0]; setUploading(true); await upload(asset.uri, { name: asset.fileName || `image_${Date.now()}.jpg`, folder: 'mobile-uploads', projectId: 'your-project-id', listener: { onComplete: (upload) => { setUploading(false); Alert.alert('Success', 'Upload complete!'); }, onError: (upload, error) => { setUploading(false); Alert.alert('Error', error.message); }, }, }); } } catch (error) { Alert.alert('Error', 'Failed to pick image'); } }; const pickAndUploadDocument = async () => { try { const result = await DocumentPicker.getDocumentAsync({ type: '*/*', copyToCacheDirectory: true, }); if (!result.canceled && result.assets[0]) { const asset = result.assets[0]; setUploading(true); await upload(asset.uri, { name: asset.name, folder: 'mobile-uploads', projectId: 'your-project-id', listener: { onComplete: (upload) => { setUploading(false); Alert.alert('Success', 'Upload complete!'); }, onError: (upload, error) => { setUploading(false); Alert.alert('Error', error.message); }, }, }); } } catch (error) { Alert.alert('Error', 'Failed to pick document'); } }; return ( <View> <TouchableOpacity onPress={pickAndUploadImage} disabled={uploading}> <Text>📷 Pick & Upload Image</Text> </TouchableOpacity> <TouchableOpacity onPress={pickAndUploadDocument} disabled={uploading}> <Text>📄 Pick & Upload Document</Text> </TouchableOpacity> {uploading && <Text>Uploading...</Text>} </View> ); }

Using ZenClientProvider (Server Integration)

If you’re using ZenClientProvider for server-side integration:

components/FileUpload.tsx
import { useZenClient } from '@filezen/react'; import * as ImagePicker from 'expo-image-picker'; import React, { useState } from 'react'; import { Alert, Text, TouchableOpacity, View } from 'react-native'; export default function FileUpload() { const { upload } = useZenClient(); const [uploading, setUploading] = useState(false); const pickAndUploadImage = async () => { try { const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.All, allowsEditing: false, quality: 1, }); if (!result.canceled && result.assets[0]) { const asset = result.assets[0]; setUploading(true); await upload(asset.uri, { name: asset.fileName || `image_${Date.now()}.jpg`, folder: 'mobile-uploads', projectId: 'your-project-id', listener: { onComplete: (upload) => { setUploading(false); Alert.alert('Success', 'Upload complete!'); }, onError: (upload, error) => { setUploading(false); Alert.alert('Error', error.message); }, }, }); } } catch (error) { Alert.alert('Error', 'Failed to pick image'); } }; return ( <View> <TouchableOpacity onPress={pickAndUploadImage} disabled={uploading}> <Text>📷 Pick & Upload Image</Text> </TouchableOpacity> {uploading && <Text>Uploading...</Text>} </View> ); }

Advanced Upload with Progress Tracking

components/AdvancedUpload.tsx
import { useFileZen } from '@filezen/react'; import * as ImagePicker from 'expo-image-picker'; import React, { useState } from 'react'; import { Alert, Text, TouchableOpacity, View } from 'react-native'; export default function AdvancedUpload() { const { upload } = useFileZen(); const [uploading, setUploading] = useState(false); const [progress, setProgress] = useState(0); const uploadWithProgress = async () => { try { const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.All, allowsEditing: false, quality: 1, }); if (!result.canceled && result.assets[0]) { const asset = result.assets[0]; setUploading(true); setProgress(0); await upload(asset.uri, { name: asset.fileName || `image_${Date.now()}.jpg`, folder: 'mobile-uploads', metadata: { userId: '12345', uploadSource: 'mobile-app' }, listener: { onStart: (upload) => { console.log('Upload started'); }, onProgress: (upload, progress) => { setProgress(progress.percent || 0); console.log(`Progress: ${progress.percent}%`); }, onComplete: (upload) => { setUploading(false); setProgress(100); Alert.alert('Success', `Upload complete! URL: ${upload.file?.url}`); }, onError: (upload, error) => { setUploading(false); setProgress(0); Alert.alert('Error', `Upload failed: ${error.message}`); }, }, }); } } catch (error) { Alert.alert('Error', 'Failed to pick image'); } }; return ( <View> <TouchableOpacity onPress={uploadWithProgress} disabled={uploading}> <Text>📷 Upload with Progress</Text> </TouchableOpacity> {uploading && ( <View> <Text>Uploading... {progress.toFixed(0)}%</Text> </View> )} </View> ); }

Configuration Options

Provider Comparison

FeatureZenStorageProviderZenClientProvider
Upload MethodDirect to FileZenDirect to FileZen
API KeyRequired (client-side)Server-side only
Server RequiredNoYes (for generating signed URLs)
Use CaseSimple apps, prototypesProduction apps with custom logic

Image Resizing (Built-in)

FileZen automatically resizes images on-the-fly using URL parameters:

// Original image <Image source={{ uri: upload.file?.url }} /> // Resized thumbnail (120x120, cover fit) <Image source={{ uri: `${upload.file?.url}?width=120&height=120&fit=cover` }} />

For all available resizing parameters, see the Dynamic Image Documentation .

The upload() method uploads files directly to FileZen from the client side. This reduces traffic usage on your server since files don’t pass through your backend.

Last updated on
© 2026 FileZen. All rights reserved.