DEV Community

Cover image for Upload Image with Expo and Firebase Cloud Storage
Fiston Emmanuel
Fiston Emmanuel

Posted on


Upload Image with Expo and Firebase Cloud Storage

User-uploaded images are common in mobile apps, how can be done with Expo and Firebase cloud storage? - let's find out the solution.

Request a device media library files access permission

Both iOS and Android require a user to grant access to the app before reading or writing to the media library.

Throughout this article, we will use expo-image-picker to request permission and upload an image from the media library.

// hasMediaLibraryPermissionGranted.js

import * as ImagePicker from "expo-image-picker";

const hasMediaLibraryPermissionGranted = async () => {
  let granted =  false;

  const permission = await ImagePicker.requestMediaLibraryPermissionsAsync();

  if (!permission.canAskAgain || permission.status === "denied") {
    granted = false;


  if (permission.granted) {
    granted = true;

  return granted;

export default hasMediaLibraryPermissionGranted

Enter fullscreen mode Exit fullscreen mode

Upload image from device media library

expo-image-picker exposes ImagePicker.launchImageLibraryAsync method to select image or video and copy to app cache then return file Universal Resource Identifier (URI) path.

// uploadImageFromDevice.js

const uploadImageFromDevice = async () => {
  let imgURI = null;
  const storagePermissionGranted = await hasMediaLibraryPermissionGranted();

  // Discard execution when  media library permission denied
  if (!storagePermissionGranted) return imgURI;

  let result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [4, 4],
    quality: 1,

  if (!result.cancelled) {
    imgURI = result.uri;

  return imgURI;

export default uploadImageFromDevice;

Enter fullscreen mode Exit fullscreen mode

Fetch uploadable image binary data.

The URI return by ImagePicker.launchImageLibraryAsync is a reference to the cached image and doesn't hold the image data.

Most cloud storage providers including Firebase Cloud Storage support a binary large object (BLOB) for uploading various files types.

Let us fetch BLOB data from an image URI .

// getBlobFroUri.js

const getBlobFroUri = async (uri) => {
  const blob = await new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.onload = function () {
    xhr.onerror = function (e) {
      reject(new TypeError("Network request failed"));
    xhr.responseType = "blob";"GET", uri, true);

  return blob;

export default getBlobFroUri

Enter fullscreen mode Exit fullscreen mode

Upload Binary data to Firebase Cloud Storage

Image BLOB data are ready now, Firebase provides a cloud object storage service with the free-tier plan for an early-stage app.

// manageFileUpload.js

import firebase from "firebase";

const manageFileUpload = async (
  { onStart, onProgress, onComplete, onFail }
) => {
  const imgName = "img-" + new Date().getTime();

  const storageRef =`images/${imgName}.jpg`);

  console.log("uploading file", imgName);

  // Create file metadata including the content type
  const metadata = {
    contentType: "image/jpeg",

  // Trigger file upload start event
  onStart && onStart();
  const uploadTask = storageRef.put(fileBlob, metadata);
  // Listen for state changes, errors, and completion of the upload.
    (snapshot) => {
      // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
      const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;

      // Monitor uploading progress
      onProgress && onProgress(Math.fround(progress).toFixed(2));
    (error) => {
      // Something went wrong - dispatch onFail event with error  response
      onFail && onFail(error);
    () => {
      // Upload completed successfully, now we can get the download URL

      uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
        // dispatch on complete event
        onComplete && onComplete(downloadURL);

        console.log("File available at", downloadURL);

export default manageFileUpload;

Enter fullscreen mode Exit fullscreen mode

Finally, Connect all pieces together


import * as React from "react";
import {
} from "react-native";
import Constants from "expo-constants";
import firebase from "./config/firebase";
import { AntDesign, Feather } from "@expo/vector-icons";
import uplodImageFromDevice from "./uploadImageFromDevice";
import getBlobFromUri from "./getBlobFromUri";
import manageFileUpload from "./manageFileUpload";
export default function App() {
  const [imgURI, setImageURI] = React.useState(null);

  const [isUploading, setIsUploading] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [remoteURL, setRemoteURL] = React.useState("");

  const [error, setError] = React.useState(null);
  const { width } = useWindowDimensions();

  const handleLocalImageUpload = async () => {
    const fileURI = await uplodImageFromDevice();

    if (fileURI) {

  const onStart = () => {

  const onProgress = (progress) => {
  const onComplete = (fileUrl) => {

  const onFail = (error) => {
  const handleCloudImageUpload = async () => {
    if (!imgURI) return;

    let fileToUpload = null;

    const blob = await getBlobFromUri(imgURI);

    await manageFileUpload(blob, { onStart, onProgress, onComplete, onFail });

  return (
    <View style={styles.container}>
      <Text style={styles.heading}>Attach and upload image</Text>
      {Boolean(imgURI) && (
            source={{ uri: imgURI }}
            style={{ width, height: width }}

      {!isUploading && (
        <View style={styles.row}>
            color={imgURI ? "green" : "black"}

          {Boolean(imgURI) && (

      {isUploading && (
        <View style={styles.uploadProgressContainer}>
          <Text> Upload {progress} of 100% </Text>

      {Boolean(remoteURL) && (
        <View style={{ paddingVertical: 20 }}>
            Image has been uploaded at
            <Text style={{ color: "blue" }}> {remoteURL} </Text>

Enter fullscreen mode Exit fullscreen mode

Expo Snack preview -

Stuck with code and need 1:1 assistance. Ping me

Top comments (2)

nonsense96 profile image

big thankss!!!!, firebase version is at 9v plus is not working as it expected, I had to downgrade to 8.3. How can I upload any kind of document? with no extension specified.

nonsense96 profile image

for those who wonder I just edited this

const Name = "file-" + new Date().toISOString();
const storageRef =${Name});

Timeless DEV post...

Git Concepts I Wish I Knew Years Ago

The most used technology by developers is not Javascript.

It's not Python or HTML.

It hardly even gets mentioned in interviews or listed as a pre-requisite for jobs.

I'm talking about Git and version control of course.

One does not simply learn git