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:
form handling in react with hooks
basic understanding of npm packages.
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.
Click on the add project and click my first project. then you will be able to type your own project name
then you will be able to type your own project name
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.
inside storage click on get started. you will see a block of code like this.
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.
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.
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.
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.
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
now we need to make an onSubmit function for the form that does some complex things
uses helper functions from an external API.
uses a lot of asynchronous code.
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
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.
Top comments (30)
refactored updated version:
it gives error
storage.ref is not a function
i've updated the snippet for new firebase >= 9.0
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...
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.
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?
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.
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.
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.
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!!!
Thank you for this tutorial it is really rich in knowledge ...
You’re welcome.
I’m glad you found it useful
Great turtorial, thank you!
Saved my ass
Thank you
Nice! great tutorial, easy to follow. Thanks, this is really helpful!