DEV Community

Cover image for Client-side image upload ⬆️ in React
Asim Dahal
Asim Dahal

Posted on

Client-side image upload ⬆️ in React

Image upload is one of the important and most used features in your web application. Let's take an example of facebook when you have to update your profile picture, you need to upload an image so that facebook can use it as your profile picture.

Today we will learn to build a simple image uploader which enables the user to select the image and display it in the browser. I hope it will be fun going through the tutorial and getting something out of it 😁. Let's go.

Setting up the project

For setting up your project, you can use either create-react-app or also you can go to CodeSandBox.

After creating your project, at first, let's create a simple file input which accepts images for upload

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  return (
    <div className="App">
      <input type="file" accept="image/*" multiple = "false" />
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Here we are creating a file input that accepts only images and will allow uploading only one file at a time.

Now, let's create a container where the uploaded image will be displayed.

function App() {
  return (
    <div className="App">
      <input type="file" accept="image/*" multiple = "false"/>
      <div
        style={{
          height: "60px",
          width: "60px",
          border: "2px dashed black"
        }}
      >
        <img
          style={{
            width: "100%",
            height: "100%",
            position: "absolute"
          }}
        />
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, we created a container where the image that is uploaded will be shown and also in the future, will be used as the image upload button.
**I wrote the inline styles for tutorial purposes.

Now the fun part let's add an onChange handler in the image input and then access the image file in the handleImageUpload method.

function App() {
  const handleImageUpload = e => {
    const [file] = e.target.files;
    if (file) {
      console.log(file);
    }
  };

  return (
    <div className="App">
      <input type="file" accept="image/*" onChange={handleImageUpload} multiple = "false" />
      <div
        style={{
          height: "60px",
          width: "60px",
          border: "1px dashed black"
        }}
      >
        <img
          style={{
            width: "100%",
            height: "100%",
            position: "absolute"
          }}
        />
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

We attach an handleImageUpload method to the onChange event in the file input we created. In the handleImageUpload method we get an e(event) object which gives access to the list of files uploaded via the file input we created. We are uploading only one image so we will be accessing the first file from the FileList and display the file for now.

Now we will attach a ref to the img we created where we can display the uploaded image using the useRef() hook.

function App() {
  const uploadedImage = React.useRef(null);
  const handleImageUpload = e => {
    const [file] = e.target.files;
    if (file) {
      console.log(file);
    }
  };

  return (
    <div className="App">
      <input type="file" accept="image/*" onChange={handleImageUpload} />
      <div
        style={{
          height: "60px",
          width: "60px",
          border: "1px dashed black"
        }}
      >
        <img
          ref={uploadedImage}
          style={{
            width: "100%",
            height: "100%",
            position: "absolute"
          }}
        />
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now we will be using the FileReader constructor in order to read the content of the file and will attach the content to the img we attached the ref to.

function App() {
  const uploadedImage = React.useRef(null);

  const handleImageUpload = e => {
    const [file] = e.target.files;
    if (file) {
      const reader = new FileReader();
      const {current} = uploadedImage;
      current.file = file;
      reader.onload = (e) => {
          current.src = e.target.result;
      }
      reader.readAsDataURL(file);
    }
  };

  return (
    <div className="App">
      <input type="file" accept="image/*" onChange={handleImageUpload} />
      <div
        style={{
          height: "60px",
          width: "60px",
          border: "1px dashed black"
        }}
      >
        <img
          ref={uploadedImage}
          style={{
            width: "100%",
            height: "100%",
            position: "absolute"
          }}
        />
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the handleImageUpload method we create a reader using FileReader constructor and then select the current from the uploadedImage ref which represents the img element. We then attach an onload event listener to the reader we created which when loaded will attach the file url it will read to the img element. We then read the file as URL using the reader.readAsDataURL() method and pass the file selected into it.

So let's see our progress till now

image uploaded

Now lets remove that ugly input button and use the box created as the image uploader

function App() {
  const uploadedImage = React.useRef(null);
  const imageUploader = React.useRef(null);

  const handleImageUpload = e => {
    const [file] = e.target.files;
    if (file) {
      const reader = new FileReader();
      const { current } = uploadedImage;
      current.file = file;
      reader.onload = e => {
        current.src = e.target.result;
      };
      reader.readAsDataURL(file);
    }
  };

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        justifyContent: "center"
      }}
    >
      <input
        type="file"
        accept="image/*"
        onChange={handleImageUpload}
        ref={imageUploader}
        style={{
          display: "none"
        }}
      />
      <div
        style={{
          height: "60px",
          width: "60px",
          border: "1px dashed black"
        }}
        onClick={() => imageUploader.current.click()}
      >
        <img
          ref={uploadedImage}
          style={{
            width: "100%",
            height: "100%",
            position: "absolute"
          }}
        />
      </div>
      Click to upload Image
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Here we create a imageUploader ref and assign it to the file input, we then hide the ugly file input element using the css display:none. After that we will attach an onClick listener to the div which clicks the file input when the div is clicked, so that means the image can be uploaded when the div is clicked. We apply some styling to our parent container so that everything appears in the center, now lets see how our output looks like.

image uploaded

You can find the completed code here

Thankyou.

You can also follow me on Twitter.

Discussion (6)

Collapse
adriaanbd profile image
Adriaan Beiertz

Hi Asim, that was a very clear way of explaining it. I've got a few questions:

  • What is e.target.result supposed to return? On my end it is undefined.
  • What were your considerations in opting for using ref?
Collapse
jakekiernan profile image
Jake Kiernan

Nice article! Why did you decide to use useRef instead of saving the file to state? @asimdahill

Collapse
adriaanbd profile image
Adriaan Beiertz

I'm also wondering the same. Furthermore, Jake did you mean internal Component state or global state via something like Context or Redux?

Collapse
dschulz1227 profile image
Damon Schulz • Edited on

Is there a way to save these images to a database so the user keeps the image displayed

Collapse
codefinity profile image
Manav Misra

Good question, and thanks for participating in the discussion. I'll have to defer to the author. But, it would appear that this article might be focused on just the client-side of things - the components, etc. Not so much the server side. For that one could consider a 'ready-made' solution such as Cloudinary, if it's an immediate need.

Collapse
nmayur profile image
Mayur Nalwala

hey,
A great article, I had one query, can you help me with how to clear the select image, like a clear button which will remove / clear selected image.

Thanks