Avatar Upload Implementation for React Native (Expo) with Supabase Storage
Overview
This implementation enables users to upload avatar images from their device (iOS/Android) to Supabase Storage using a React Native (Expo) app. It is designed to work reliably across platforms, following Supabase's official recommendations for React Native file uploads.
Why This Approach?
1. React Native & Supabase Storage Compatibility
- Problem: Supabase's
storage.from().upload()does not support uploadingBlob,File, orFormDataobjects in React Native as it does in web environments. - Solution: The recommended approach is to read the image file as a Base64 string, decode it to an
ArrayBuffer, and upload that buffer to Supabase Storage.
2. Expo FileSystem for File Access
- Expo FileSystem is used to read the image file from the device, regardless of whether the URI is a
file://or an Androidcontent://URI. - For Android
content://URIs, the file is first copied to a temporary cache directory to ensure it can be accessed by FileSystem.
3. File Type and Size Validation
- Only JPEG and PNG images are allowed, enforced by checking the file extension.
- (Optional) You can add file size checks if needed.
Example Usage
1. Picking an Image
import * as ImagePicker from 'expo-image-picker';
const pickAvatarImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.8,
});
if (!result.canceled && result.assets.length > 0) {
return result.assets[0].uri;
}
return null;
};
2. Uploading the Image
const updateAvatar = useUpdateAvatarNative();
const handleAvatar = async () => {
const uri = await pickAvatarImage();
if (!uri) return;
updateAvatar.mutate({ id: userId, uri });
};
3. The Upload Hook (Key Steps)
// ...existing code...
const base64 = await FileSystem.readAsStringAsync(fetchUri, {
encoding: FileSystem.EncodingType.Base64,
});
const arrayBuffer = decode(base64);
const { data, error } = await supabase.storage
.from('avatars')
.upload(fileName, arrayBuffer, {
upsert: true,
contentType: contentType,
});
// ...existing code...
Key Points
- Why not Blob/File?: Supabase Storage's JS client does not support these types in React Native. Use ArrayBuffer from base64 instead.
- Why copy Android content URIs?: Android's
content://URIs are not always readable directly. Copying to cache ensures compatibility. - Why validate file type?: To prevent unsupported or potentially unsafe uploads and to match your Supabase Storage bucket's allowed MIME types.
References
If you need to support additional file types or add file size validation, update the validation logic in the hook accordingly.