DEV Community

loading...
Cover image for Building a custom React image upload widget with progress bar.

Building a custom React image upload widget with progress bar.

emmanx profile image Emmax ・3 min read

In this article, I'll be walking you through how to create a simple custom react file upload widget with a progress upload bar.

Prerequisites

This article assumes some previous knowledge of basic React and making HTTP requests.

For the purpose of this article, I have created a very basic API to handle the file upload on the server using Nest.js and deployed it to Heroku. Here's the endpoint where we'll be making our POST request to - https://nestjs-upload.herokuapp.com/ Uploaded files are automatically deleted every 5mins and I have implemented rate-limiting so the upload service can survive public usage for testing.

Setup a react project using

yarn create react-app upload-widget // NPM works too
Enter fullscreen mode Exit fullscreen mode

Feel free to clean up the setup however you like and remove whatever files you don't need.

The API call

Install Axios by running

yarn add axios
Enter fullscreen mode Exit fullscreen mode

Proceed to create a file called upload.js can add the following code to it.

import axios from "axios";

const uploadFile = async (body, setPercentage) => {
  try {
    const options = {
      onUploadProgress: (progressEvent) => {
        const { loaded, total } = progressEvent;
        const percent = Math.floor((loaded * 100) / total);

        if (percent <= 100) {
          setPercentage(percent);
        }
      }
    };

    const { data } = await axios.post(
      "https://nestjs-upload.herokuapp.com/",
      body,
      options
    );

    return data;
  } catch (error) {
    console.log(error.message);
  }
};

export default uploadFile;

Enter fullscreen mode Exit fullscreen mode

What's going on?

For the most part, we are simply making a POST request to an endpoint that's supposed to process our request and send back some response... The part that might seem unfamiliar is the

onUploadProgress: (progressEvent) => {
  // Do whatever you want with the native progress event
}
Enter fullscreen mode Exit fullscreen mode

onUploadProgress allows handling of progress events for uploads. There is a onDownloadProgress which I believe does the same thing but for downloads.

const { loaded, total } = progressEvent;
Enter fullscreen mode Exit fullscreen mode

We are destructuring loaded and total from the progressEvent parameter that we are given access to, where loaded refers to how much has been uploaded and total is the total size to be uploaded

const percent = Math.floor((loaded * 100) / total);

if (percent <= 100) {
  setPercentage(percent);
}
Enter fullscreen mode Exit fullscreen mode

And finally, we are using some basic maths to calculate the percent and calling a setPercentage function which will be passed in as an argument from wherever we call our upload function.

That wraps up the first part of the task - writing the upload logic. It's time to implement the React component that will use this upload function.

I'll just dump the code then proceed to explain what's happening.

import React, { useState } from "react";
import uploadFile from "./upload";

function UploadWidget() {
  const [percentage, setPercentage] = useState(0);

  const handleFile = async (e) => {
    try {
      const file = e.target.files[0];
      const formData = new FormData();
      formData.append("image", file);

      await uploadFile(formData, setPercentage);
      setPercentage(0);
    } catch (err) {
      setPercentage(0);
      console.log(err);
    }
  };

  return (
    <div>
      <p>Click here to browse</p>
      <input onChange={handleFile} type="file" />
      <p>{percentage} %</p>
    </div>
  );
}

export default UploadWidget;

Enter fullscreen mode Exit fullscreen mode

Breakdown

We've initialized a piece of state to keep track of the upload percentage which would be updated when the onUploadProgress event is fired because we passed the setPercentage function into our upload function. Below is a code sandbox of a working implementation with the CSS styling included.

I hope you found this helpful. Share your thoughts in the comments section.

PS

The goal was to keep this short and focused on the onUploadProgress event and how to track the upload progress, you can build up on this by adding file type and size checks and restructuring the code into a nice little reusable useUpload hook for your project(s).

Discussion

pic
Editor guide