DEV Community

Cover image for How to do image upload with firebase in react.
Tallan Groberg for ITNEXT

Posted on • Edited on

How to do image upload with firebase in react.

As a web developer, giving your user's image uploading capability is a right a passage. I'm writing this article because of how frustrating it was for me to do this in my e-commerce site and I hope this will come in handy for a react dev out there trying to make it happen.

almost all e-commerce and social media websites require users to be able to upload image files so it's essential

Today we are going to learn how to enable our users to upload single images in react using firebase as a storage API.

Prerequisites:

  1. form handling in react with hooks

  2. basic understanding of npm packages.

  3. a firebase account

5.optional: unix command line. Ill be using it for this tutorial.

overview: we are going to make a form that will take a file and save it to state. upload the file to firebase storage, firebase will give us a custom URL for that image and we will save that image to an object with a key, value pair. a title and an imgUrl, the imgUrl will be a string provided to us from firebase storage.

go to firebase console and make a new project. if you have a firebase account your console page should look something like this.

Alt Text

Click on the add project and click my first project. then you will be able to type your own project name

Alt Text

then you will be able to type your own project name

Alt Text
name it whatever you want

In the second part of the process you will be able to opt into google analytics. click continue with the preferences you decide.

if you do opt into the analytics then choose the default account.

now you should be on your firebase landing page. On the left side of the screen click storage.

Alt Text

inside storage click on get started. you will see a block of code like this.

Alt Text

we will make a place in our react project this code will not be a part of that. this is strictly firebase side code.

if you read the text you will notice that it is configured to upload with an authenticated user. since we are doing this without auth for sake of brevity, click next.

choose a storage location. Ideally this location should be where your users are most likely to use your project.

Alt Text

after you choose a location, a default bucket should be created to store your images.

in the bucket storage page that was just created, go to your storage bucket rules.

Alt Text

now we see the code from earlier. lets change that code to work without auth. __this is a crucial part of getting this to work!!!!!!!!!!!

change it from this.

Alt Text
you have to use firebase auth for this code to work.

to

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {

//this is the part we changed...
      allow read, write: if true;
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

you should be prompted to publish your changes. I know it can take up to a day or so to take effect. That's why we are doing this first in the tutorial.

It may happen sooner but it took firebase a while for me.

the next step is getting a webSDK for our project so lets register this app.

go to the project overview in the top left corner in the navbar. From that page register this as a web app. and give the app a nickname.

if you scroll down you should see an SDK page.

like this:


<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-analytics.js"></script>

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "super secret keys.....asgvegxgevergfvr",
    authDomain: "tallans-imageupload-tutorial.firebaseapp.com",
    databaseURL: "https://tallans-imageupload-tutorial.firebaseio.com",
    projectId: "tallans-imageupload-tutorial",
    storageBucket: "tallans-imageupload-tutorial.appspot.com",
    messagingSenderId: "super secret keys.....asgvegxgevergfvr",
    appId: "super secret app id....adsfa;lsdkjf",
    measurementId: "super secret as;dlkfjal;dskjf"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();
</script>

Enter fullscreen mode Exit fullscreen mode

leave this page open and we will come back to it, this is the firebase side configs that we need to get started. we will make a place for firebase in our react app.

lets make a react app.

create-react-app firebase-imageupload
Enter fullscreen mode Exit fullscreen mode

open this in your favorite text editor. It's not required but I will be using vs code.

enter the project directory and make sure everything is working.

cd firebase-imageupload && npm start
Enter fullscreen mode Exit fullscreen mode

you should see the boilerplate web page that react comes with.

Alt Text

make your react app a blank by changing the App.js to this.

import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">
      blank and ready for image upload. 
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

now install the firebase npm package.

npm i firebase
Enter fullscreen mode Exit fullscreen mode

let's make a directory for the firebase config on the react side. add an index.js file to that directory

mkdir src/firebase && touch src/firebase/firebase.js

Enter fullscreen mode Exit fullscreen mode

add the imports at the top of your firebase.js file.

import firebase from 'firebase/app'
import 'firebase/storage'
Enter fullscreen mode Exit fullscreen mode

below the imports add the firebase SDK.

firebase.js

 var firebaseConfig = {
    apiKey: "super secret keys.....asgvegxgevergfvr",
    authDomain: "tallans-imageupload-tutorial.firebaseapp.com",
    databaseURL: "https://tallans-imageupload-tutorial.firebaseio.com",
    projectId: "tallans-imageupload-tutorial",
    storageBucket: "tallans-imageupload-tutorial.appspot.com",
    messagingSenderId: "super secret keys.....asgvegxgevergfvr",
    appId: "super secret app id....adsfa;lsdkjf",
    measurementId: "super secret as;dlkfjal;dskjf"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
//analytics is optional for this tutoral 
  firebase.analytics();

Enter fullscreen mode Exit fullscreen mode

below the firebase.initializeApp(firebaseConfig); add initialize the storage as a variable

const storage = firebase.storage()

Enter fullscreen mode Exit fullscreen mode

at the bottom of the file lets export everything together.


export  {
   storage, firebase as default
 }

Enter fullscreen mode Exit fullscreen mode

now we have a way of using firebase storage functionality through the whole react app.

go to the App.js and lets make the form to get the file and an input field with a type='file'

App.js

//add useState for handling the image as a file and then the image as a url from firebase
import React, {useState} from 'react'
import {storage} from "./firebase/firebase"
//add import for storage 
function App() {
  return (
    <div className="App">
//form for handling file upload
      <form>
        <input 
// allows you to reach into your file directory and upload image to the browser
          type="file"
        />
      </form>

    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

now between the function app() { and return (

Add the the useState's

const allInputs = {imgUrl: ''}
    const [imageAsFile, setImageAsFile] = useState('')
    const [imageAsUrl, setImageAsUrl] = useState(allImputs)
Enter fullscreen mode Exit fullscreen mode

We are set up to add things to the form for the image. Such as a title, comment, post, description ect.. we will see how after the image upload is functional.

It may seem strange to add the image url as a object key: value, pair and that we are uploading a file to a string but this is how I got it working and if anyone has a better way please point to that resource in the comments.

now we need to make the function to handle the image upload as a file so that we can stage it for a post request to firebase.

console.log(imageAsFile)
 const handleImageAsFile = (e) => {
      const image = e.target.files[0]
      setImageAsFile(imageFile => (image))
  }

Enter fullscreen mode Exit fullscreen mode

then add the function to the input field

 <input 
   type="file"
   onChange={handleImageAsFile}
 />


Enter fullscreen mode Exit fullscreen mode

now choose an image file from your computer directory and see how it goes.

check the console once the image is uploaded. option + command + j is the hot key for devtools in chrome.

you should see something like this

Alt Text

now we need to make an onSubmit function for the form that does some complex things

  1. uses helper functions from an external API.

  2. uses a lot of asynchronous code.

  3. gets a response from firebase and sets an imageUrl as a string to an object in state.

make the skeleton of the function

const handleFireBaseUpload = e => {
  e.preventDefault()
console.log('start of upload')
// async magic goes here...

}
Enter fullscreen mode Exit fullscreen mode

I'm going to add console.logs so you can see every step of the way and diagnose problems as they happen.

lets add a button to the form and the onSubmit to the top of the form tag.

When you press the button it will console log start of upload.

the form will look like this.

  <form onSubmit={handleFireBaseUpload}>
        <input 
          type="file"
          onChange={handleImageAsFile}
        />
        <button>upload to firebase</button>
      </form>

Enter fullscreen mode Exit fullscreen mode

lets start with some error handling

 // async magic goes here...
    if(imageAsFile === '' ) {
      console.error(`not an image, the image file is a ${typeof(imageAsFile)}`)
    }

Enter fullscreen mode Exit fullscreen mode

the error message will tell you if you didn't upload an image or it was null or undefined.

make sure that you are in the part of your console that will display errors since we are using console.error and not console.log

below that now we can start the uploading process.

we are creating an uploadTask variable add this right below the if statment

const uploadTask = storage.ref(`/images/${imageAsFile.name}`).put(imageAsFile)
Enter fullscreen mode Exit fullscreen mode

now if you check your firebase console you will see that the image is there.

nice

now, below the const uploadTask, grab the image from firebase as an imageUrl.

with the uploadTask.on( //internet magic inside ) method

this will run through a snapshot of what is happening which we will console.log

we will add an error handler after the snapshot is taken.

use an anonymous function to do the rest...

grab a storage reference as a child.

get the download URL from the file path on the firebase side.

then set the imageAsUrl key with what firebase gives us as the value.

this function will look like this.

//initiates the firebase side uploading 
    uploadTask.on('state_changed', 
    (snapShot) => {
      //takes a snap shot of the process as it is happening
      console.log(snapShot)
    }, (err) => {
      //catches the errors
      console.log(err)
    }, () => {
      // gets the functions from storage refences the image storage in firebase by the children
      // gets the download url then sets the image from firebase as the value for the imgUrl key:
      storage.ref('images').child(imageAsFile.name).getDownloadURL()
       .then(fireBaseUrl => {
         setImageAsUrl(prevObject => ({...prevObject, imgUrl: fireBaseUrl}))
       })
    })
Enter fullscreen mode Exit fullscreen mode

that was a huge sequence of events so let me give you the whole function.

const handleFireBaseUpload = e => {
      e.preventDefault()
    console.log('start of upload')
    // async magic goes here...
    if(imageAsFile === '') {
      console.error(`not an image, the image file is a ${typeof(imageAsFile)}`)
    }
    const uploadTask = storage.ref(`/images/${imageAsFile.name}`).put(imageAsFile)
    //initiates the firebase side uploading 
    uploadTask.on('state_changed', 
    (snapShot) => {
      //takes a snap shot of the process as it is happening
      console.log(snapShot)
    }, (err) => {
      //catches the errors
      console.log(err)
    }, () => {
      // gets the functions from storage refences the image storage in firebase by the children
      // gets the download url then sets the image from firebase as the value for the imgUrl key:
      storage.ref('images').child(imageAsFile.name).getDownloadURL()
       .then(fireBaseUrl => {
         setImageAsUrl(prevObject => ({...prevObject, imgUrl: fireBaseUrl}))
       })
    })
    }

Enter fullscreen mode Exit fullscreen mode

now if you console.log imgAsUrl you will see this

Alt Text
look at the very last console output

now lets display our image to the screen.

below the form enter this code.


//end of form 

<img src={imageAsUrl.imgUrl} alt="image tag" />

//closing tag for div

Enter fullscreen mode Exit fullscreen mode

there you have it you can now upload images. if you want to give them there images titles you can add keys: to the initState object.

you can make another form and reference the image from firebase

Credits:

I relied on this youtube video but it was in classes and I used hooks.

conclusion:

image uploads are like everything else in programming. with time and practice, they get easier.

firebase is a wonderful developer tool and I encourage you to check out some of there other features.

once again if someone knows a better way of doing this please list this in the comments below. if you have any questions please don't be afraid to leave a comment ether.

Top comments (30)

Collapse
 
samuelkarani profile image
Samuel Karani • Edited

refactored updated version:

import React, { useState } from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/storage";

export const storage = firebase.storage();

export default function App() {
  const [file, setFile] = useState(null);
  const [url, setURL] = useState("");

  function handleChange(e) {
    if (e.target.files[0])
        setFile(e.target.files[0]);
  }

  function handleUpload(e) {
    e.preventDefault();
    const path = `/images/${file.name}`;
    const ref = storage.ref(path);
    await ref.put(file);
    const url = await ref.getDownloadURL();
    setURL(url);
    setFile(null);
  }

  return (
    <div>
      <form onSubmit={handleUpload}>
        <input type="file" onChange={handleChange} />
        <button disabled={!file}>upload to firebase</button>
      </form>
      <img src={url} alt="" />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
muhammadanasalvi18 profile image
Anas Alvi

it gives error
storage.ref is not a function

Collapse
 
samuelkarani profile image
Samuel Karani

i've updated the snippet for new firebase >= 9.0

Collapse
 
alaomichael profile image
Michael Alao

Thanks for the tutorial. Please can you guide me on how to use this in a form, an return the image after sumbitting the image to firebase, so that I can use it in a list.

Collapse
 
tallangroberg profile image
Tallan Groberg

I’m sorry for the late response.

The best way I have used this in forms is to have the image upload input outside the form that contains the text
inputs

Then save the imageAsUrl to an object with The text inputs.

So it would look something like this.

Usestate({title: “”, description: “”, imgUrl: “some return from firebase”})

The title and description get saved on a separate form as the img url

I hope that helps and I’m sorry I was so late on the response.

Collapse
 
lydiacha profile image
Lydia Chatziioannou • Edited

Hi Tallan, I'm a complete beginner and I'm afraid I'm not sure what you mean by this comment. I also want to use the uploadImage within a form where I have other text input fields. Is it possible for you to create another tutorial that does exactly that? And also how to display all the data from the form on a different page? My main is problem is... if I'm creating a post with a title, content and then an image how does the image know that it belongs to that specific post with that title and content if the title and content are saved in Firestore and the image is stored in storage? I would appreciate a tutorial on this, there's nothing else out there from what I've seen so far. Thanks!

Thread Thread
 
tallangroberg profile image
Tallan Groberg

Ok, I will see if I can make a part 2 to this tutorial so that it’s easier to see how image uploads fit into the bigger picture with other form data.

This tutorial was designed to be as simple as possible for doing an image upload, but I am seeing the need to show everyone how to use this in a way that you would naturally on a website.

Thanks for the feedback!!

Thread Thread
 
lydiacha profile image
Lydia Chatziioannou • Edited

Amazing, thank you!

Any chance you can create the tutorial this month? ☺️

Collapse
 
alaomichael profile image
Michael Alao

Thank you so much for the reply.
Please how do I restrict user to view only their data in a React ,Redux application? Thank you.

Thread Thread
 
tallangroberg profile image
Tallan Groberg

You will have to make a user auth, I have a tutorial on how to do so with firebase if you want to continue learning more about this kind of stuff.

I don’t know how to do so with redux but it should be fairly similar to react context.

User auth can be very tricky and I would suggest going through my firebase tutorial because that is the easiest way I know how to give users identity on my apps

Thread Thread
 
alaomichael profile image
Michael Alao

Please can you send me the link to your tutorial? Thank you.

Thread Thread
 
tallangroberg profile image
Tallan Groberg

Sure,

Let me know if this works for you!!

Good luck

dev.to/itnext/user-auth-with-fireb...

Collapse
 
francisrod01 profile image
Francis Rodrigues

Hi there, thanks for this tutorial.
Could you suggest the best practice to load a list of posts (with firebase images) in the background?

Collapse
 
tallangroberg profile image
Tallan Groberg

So there are a lot of ways you can do that. You could save the image string to a larger object and then set the background of the div in the style={ backGroundUrl: “imgUrl”}

I don’t know of a good resource for how to do this but if you got my tutorial working you got the hardest part of the process is complete.

Good luck

Collapse
 
francisrod01 profile image
Francis Rodrigues

Thanks for reaching out.
I'm more interested in the part of saving and fetching this data.
Which strategy do you use to do that better?

I'm not sure if it will be a good practice fetching the storage resource every time a user navigates back in a list of records with its storage url.

Thread Thread
 
tallangroberg profile image
Tallan Groberg • Edited

I would suggest multer if you are familiar with node. I'm not sure of a relational database option but building a relational backend would be the very best practice, this article talks about the pros and cons.

You don't technically own your data with firebase unless you save it somewhere else after it's uploaded to firebase and so I personally suggest using multer unless you have some background in relational database building. With these options, you will have more of a say in what happens to the images than what I teach in this tutorial.

Also, It should be a faster API call.

Does that answer your question?

Thread Thread
 
francisrod01 profile image
Francis Rodrigues

Thanks for this interesting package. I'll have a better look at it later.
I was talking about something related to firestore.

You know that Firebase has the Storage service, right? gs://xxxxxx-app.appspot.com

Being more specific, so I've asked if you will upload a file on Firebase Storage service and save its file URL into Firestore or only the file name and loads it every time the user opens the page. That's my question.

Thread Thread
 
tallangroberg profile image
Tallan Groberg

I’m currently working on a tutorial about how to do what you just described. Since I know this tutorial is only good for one small piece of functionality. I’m hoping to release something that ties together using my stuff about firebase specifically.

There is a lot of really good material about firebase on dev.to and the broader web that might be able to get you further on your project.

Thread Thread
 
francisrod01 profile image
Francis Rodrigues

Great!! You can count on me getting beginners/intermediates levels of JavaScript programmers with Firebase questions and bringing it to you. I'm not the only one with such doubts, I can say.

Collapse
 
jailsonpaca profile image
jailsonpaca

Thank You! God Bless you! i am using this tool with React Admin framework.

Collapse
 
lynnknapp profile image
Lynn Knapp

Have I told you lately how awesome you are Tallan, your the best.

Collapse
 
tallangroberg profile image
Tallan Groberg

Your welcome Lynn, your really awesome too!

Collapse
 
bezen13 profile image
BeZen13

Looks great man

Collapse
 
wenzf profile image
wenzf

Great example to make this work. Understandable but reduced to the max. Perfect! Thank you very much for this!

Collapse
 
tallangroberg profile image
Tallan Groberg

Thanks, I’m glad you found it helpful!!!

Collapse
 
trakyrichard profile image
Traky Richard BLM

Thank you for this tutorial it is really rich in knowledge ...

Collapse
 
tallangroberg profile image
Tallan Groberg

You’re welcome.

I’m glad you found it useful

Collapse
 
lucafahrni profile image
Luca Fahrni

Great turtorial, thank you!

Collapse
 
lucafahrni profile image
Luca Fahrni

Saved my ass
Thank you