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

How to do image upload with firebase in react.

tallangroberg profile image Tallan Groberg Updated on ・8 min read

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;
    }
  }
}

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>

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

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

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;

now install the firebase npm package.

npm i firebase

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

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

import firebase from 'firebase/app'
import 'firebase/storage'

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();

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

const storage = firebase.storage()

at the bottom of the file lets export everything together.


export  {
   storage, firebase as default
 }

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>
  );
}

now between the function app() { and return (

Add the the useState's

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

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))
  }

then add the function to the input field

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


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...

}

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>

lets start with some error handling

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

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)

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}))
       })
    })

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}))
       })
    })
    }

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

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.

Posted on by:

tallangroberg profile

Tallan Groberg

@tallangroberg

I love to write, I love all things tech I recently published a novel.

ITNEXT

ITNEXT is a platform for software developers, engineers, IT architects, system engineers and IT enthousiasts to share knowledge, connect and connect.

Discussion

pic
Editor guide
 

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.

 

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.

 

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!

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!!

Amazing, thank you!

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

 

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.

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

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

Sure,

Let me know if this works for you!!

Good luck

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

 

refactored version:

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

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

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

  function handleUpload(e) {
    e.preventDefault();
    const uploadTask = storage.ref(`/images/${file.name}`).put(file);
    uploadTask.on("state_changed", console.log, console.error, () => {
      storage
        .ref("images")
        .child(file.name)
        .getDownloadURL()
        .then((url) => {
          setFile(null);
          setURL(url);
        });
    });
  }

  return (
    <div>
      <form onSubmit={handleUpload}>
        <input type="file" onChange={handleChange} />
        <button disabled={!file}>upload to firebase</button>
      </form>
      <img src={url} alt="" />
    </div>
  );
}```


 

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

 

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

 

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.

 

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

 

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

 

Your welcome Lynn, your really awesome too!

 

Looks great man

 

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

 

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

 

Nice! great tutorial, easy to follow. Thanks, this is really helpful!