How to do image upload with firebase in react.
Tags: firebase, imageupload, react, javascript
Published: 2019-11-30 04:54:01

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](https://the-stor-e.herokuapp.com) 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

3. basic understanding of npm packages.

4. [a firebase account](https://firebase.google.com/?gclid=Cj0KCQiAoIPvBRDgARIsAHsCw092g0PcazQOPyn4_D3upjHAwnXOBmjV5oMrtRiLrMygl-MSKikyXwoaAjRSEALw_wcB) 

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](https://console.firebase.google.com/u/0/) and make a new project. if you have a firebase account your console page should look something like this.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/q05v1whnfk4zatp11fhl.png)


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

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/p0b5kj7mn4l2pmgpz4wq.png) 



then you will be able to type your own project name

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/8ue35srdnnk1zkoi2sa2.png)
_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](https://thepracticaldev.s3.amazonaws.com/i/7w9vnv2s1heteg1jvosq.png)

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

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/5zcml0x0uvu55i7kqywc.png)


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](https://thepracticaldev.s3.amazonaws.com/i/8ter55fvdadqgfz1r9zg.png)

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](https://thepracticaldev.s3.amazonaws.com/i/8u9406sqztbb4glqjryt.png)



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](https://thepracticaldev.s3.amazonaws.com/i/eap9skyytw6d4prgr3lk.png)
_you have to use firebase auth for this code to work._



__to__

```javascript
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: 

```javascript

<!-- 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](https://code.visualstudio.com/download).

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](https://thepracticaldev.s3.amazonaws.com/i/pwsk2cgmg1ea3g2rj9cm.png)


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

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


```javascript
import firebase from 'firebase/app'
import 'firebase/storage'
```


below the imports add the firebase SDK.
 
_firebase.js_
```javascript
 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

```javascript
const storage = firebase.storage()

```

at the bottom of the file lets export everything together. 

```javascript

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_

```javascript
//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

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

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

```

then add the function to the input field 

```javascript
 <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](https://thepracticaldev.s3.amazonaws.com/i/sirn61uom693cxx0b8oi.png)

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 

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

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

```

lets start with some error handling

```javascript
 // 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

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

```javascript
//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. 

```javascript
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](https://thepracticaldev.s3.amazonaws.com/i/xtezby3qdzcop1uqrn2b.png)
_look at the very last console output_

now lets display our image to the screen. 

below the form enter this code. 


```javascipt

//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](https://www.youtube.com/watch?v=YR4roPyfDQU&t=1378s) 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.