Simple Image Upload with React

Jesse Heaslip
codeburst
Published in
6 min readSep 15, 2018

--

At the coding bootcamp where I teach, “How do I upload images with React?” is a question that comes up repeatedly. As with any programming problem there are a million ways to achieve this outcome with various tradeoffs.

Below is a way that I feel accounts for many possible scenarios you might encounter while maintaining a streamlined codebase.

Before we get too far, here is a demo of what we are going to be building today. And the Github repo if you want to skip ahead.

Presentation (aka dumb) Components

This project is bootstrapped with Create React App. I am assuming you already have some experience with CRA. If not, here is a great tutorial.

The application has 3 possible states we need to account for:

  1. Loading state where the images are being uploaded
  2. Images state where there are images to display
  3. Initial state where there are buttons to push to start the process

We are going to set up each of those states in their own presentation component. First things first, Spinner.js.

Not a lot to this one, it shows while the images are uploading. Let’s move on to Images.js which renders when there are images to show.

A little more interesting here, we are passing in the images as a prop and then mapping over those images to produce the required jsx. But this is still pretty bland. Lets take a peek at our final presentation component, Buttons.js, and then we can move into the real action.

Although there is a ton of overlap of code between the two buttons, it just didn’t make sense (to me) to break the differences out to an Array of Objects and then map over that. If there was an additional button, I probably would have.

Here we are setting up our two buttons to either upload a single image file or multiple images. Still no big surprises. Its time to move on to the meat of this project in the App.js file.

App.js

This is were the client starts to come alive. We have a couple of methods on the App class to help us manage the image upload workflow, onChange and removeImage.

This the core of the project so lets break it down a little bit.

onChange — This is where the real action happens. We are extracting the files to be uploaded out of the DOM and shipping them off to our server in a fetch request. It also allows us to update the state of our application to show that something is happening (spinner) or show the images when they come back successfully.

removeImage — This allows us to pull images off the DOM and put the application back in the state where we can upload more images. Its also helpful to deal with errors that happen in the application. More on that below.

Thats it! We are now ready to move on to writing our server.

Server

The easiest way to get started with storing images is to have a third party host the images and we just use a string url reference to that image to display it in the application. Cloudinary is a great service that provides a generous amount of storage and transfer on their free plan. Go ahead and sign up to get your api keys.

After signing up for Cloudinary we need to plug those keys into a .env file on the server. If you need a little help with that, please read this.

All set up? Time to get into the server code.

And that is it! Because we have put our images into an Array on the client side (even if it is just a single image) we can use the same endpoint to process them on the server.

If you wanted to break it into two separate endpoints, the single upload route could look something like this.

The form data comes in an Object with keys that will vary by the type of file that was uploaded, so we need to put a little ugly with the double Object.values to get down to the path of the single file. But it is still pretty approachable.

And that is all for the server!

Bonus - Breaking the Code

As developers, we should be constantly thinking about how a user may break our program and trying to anticipate those bugs before they occur. In the case of uploading an image I came up with a few ways a user could bring this application down (with a big hat tip to @jasont_10452 for his expert skills as a professional application breaker).

  1. Uploading too many images and blowing the limits on the free Cloudinary account
  2. Uploading an image with the wrong file extension
  3. Uploading an image that is too large
  4. Uploading an image where the file extension has been intentionally changed and Cloudinary could not process it. (eg. changing a .js file to a .jpeg extension)
  5. Uploading an image where the file extension has been intentionally changed and Cloudinary could process it, but the DOM could not render the file (eg. changing a .pdf file to use a .png extension)

Let’s take a quick look at how to manage those breaking user interactions:

You will notice the code in the repo for handling errors is a little different than what I have included above. It is more robust in how it manages negative outcomes (for example, using a toast component to provide feedback to the user).

This is intentional, so we can focus on the core workflow in this Medium post and still have access to a more production ready solution when you use the Github repo as your starter.

Takeaways

I hope this helped you get your head wrapped around uploading images (and handling errors) with React. Would love to hear if you have a different approach to the problem or a way to improve this solution.

If this tutorial helped you, go ahead and high +5 (✋) the clap button below to help share this with other folks who may also enjoy this post.

Thanks

✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.

--

--