React Drag and Drop File Upload example
Tags: react, webdev, javascript, todayilearned
Published: 2022-12-13 11:50:00

In this React tutorial, I will show you way to build Drag and Drop File Upload example with React Hooks using <a href="https://github.com/react-dropzone/react-dropzone">react-dropzone</a>, <a href="https://github.com/axios/axios">Axios</a> and <a href="https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html">Multipart</a> File for making HTTP requests, Bootstrap for progress bar and display list of files' information (with download url).

From [BezKoder](https://www.bezkoder.com/drag-drop-file-upload-react-hooks/).

<h2>Overview</h2>
We’re gonna create a Drag and Drop File Upload by React example in that user can:
<ul>
	<li>drag file and drop it into Drop zone</li>
	<li>see the upload process (percentage) with progress bar</li>
	<li>view all uploaded files</li>
	<li>download link to file when clicking on the file name</li>
</ul>


![react-drag-and-drop-file-upload-example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oiyukmfb6go40aggxyom.png)

Right after drag and drop file into the Dropzone:

![react-drag-and-drop-file-upload-dropzone](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pdc8xttzad791tgw5ens.png)

Click on <strong>Upload</strong> button:

![react-drag-and-drop-file-upload-progress-bar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wj8nnq1k4gt02h36vnln.png)

<h2>Technology</h2>
<ul>
	<li>React 18/17/16</li>
	<li>Axios</li>
	<li>react-dropzone 11.4.0</li>
	<li>Bootstrap 4</li>
</ul>

<h2>Rest API for File Upload & Storage</h2>
Here is the API that our React App will work with:

<div class="table-responsive">
<table class="table table-hover  table table-hover" width="100%">
<thead>
<tr>
<th>Methods</th>
<th>Urls</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>POST</td>
<td>/upload</td>
<td>upload a File</td>
</tr>
<tr>
<td>GET</td>
<td>/files</td>
<td>get List of Files (name & url)</td>
</tr>
<tr>
<td>GET</td>
<td>/files/[filename]</td>
<td>download a File</td>
</tr>
</tbody>
</table>
</div>

You can find how to implement the Rest APIs Server at one of following posts:
- <a href="https://bezkoder.com/node-js-express-file-upload/">Node.js Express File Upload Rest API example</a>
- <a href="https://www.bezkoder.com/node-js-upload-store-images-mongodb/">Node.js Express File Upload to MongoDB example</a>
- <a href="https://bezkoder.com/google-cloud-storage-nodejs-upload-file/">Node.js Express File Upload to Google Cloud Storage example</a>
- <a href="https://bezkoder.com/spring-boot-file-upload/">Spring Boot Multipart File upload (to static folder) example</a>

Or: <a href="https://bezkoder.com/spring-boot-upload-file-database/">Spring Boot Multipart File upload (to database) example</a>

<h2>React Drag and Drop File Upload Application</h2>
After building the React.js project is done, the folder structure will look like this:

![react-drag-and-drop-file-upload-project-structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w9485nhlmf3ycmsee4md.png)

Let me explain it briefly.

- <em>FileUploadService</em> provides functions to save File and get Files using Axios.
- <em>FileUpload</em> contains file upload dropzone, progress bar, display of list files.
- <em>App.js</em> is the container that we embed all React components.

- <em>http-common.js</em> initializes Axios with HTTP base Url and headers.
- We configure port for our App in <em>.env</em>

<h2>Setup Drag and Drop File Upload Project</h2>
Open cmd at the folder you want to save Project folder, run command:
<code>npx create-react-app drag-drop-file-upload-react-hooks</code>

After the process is done. We create additional folders and files like the following tree:
<hr/>
public
src
--components
----FileUpload.js
--services
----FileUploadService.js
--App.css
--App.js
--index.js
package.json

<h2>Import Bootstrap to React Drag Drop File Upload App</h2>
Run command: <code>yarn add bootstrap@4.6.0</code>
Or: <code>npm install bootstrap@4.6.0</code>.

Open <strong>src</strong>/<em>App.js</em> and modify the code inside it as following-
```js
import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";

function App() {
  return (
    ...
  );
}

export default App;
```

<h2>Initialize Axios for React HTTP Client</h2>
Let's install <em>axios</em> with command:
<code>yarn add axios</code> or <code>npm install axios</code>.

Under <strong>src</strong> folder, we create <em>http-common.js</em> file with following code:

```js
import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:8080",
  headers: {
    "Content-type": "application/json"
  }
});
```

You can change the <code>baseURL</code> that depends on REST APIs url that your Server configures.

<h2>Create Service for File Upload</h2>
This service will use Axios to send HTTP requests.
There are 2 functions:
<ul>
	<li><code>uploadFile(file)</code>: POST form data with a callback for tracking upload progress</li>
	<li><code>getFiles()</code>: GET list of Files' information</li>
</ul>

<strong>services</strong>/<em>FileUploadService.js</em>
```js
import http from "../http-common";

export const uploadFile = (file, onUploadProgress) => {
  let formData = new FormData();

  formData.append("file", file);

  return http.post("/upload", formData, {
    headers: {
      "Content-Type": "multipart/form-data",
    },
    onUploadProgress,
  });
};

export const getFiles = () => {
  return http.get("/files");
};
```
- First we import Axios as <code>http</code> from <em>http-common.js</em>.

- Inside <code>uploadFile()</code> method, we use <code>FormData</code> to store key-value pairs. It helps to build an object which corresponds to HTML form using <code>append()</code> method. 

- We pass <code>onUploadProgress</code> to exposes progress events. This progress event are expensive (change detection for each event), so you should only use when you want to monitor it.

- We call Axios <code>post()</code> to send an HTTP POST for uploading a File to Rest APIs Server and <code>get()</code> method for HTTP GET request to retrieve all stored files.

<h2>Install react-dropzone</h2>
Add <strong>react-dropzone</strong> module into project with command:
- <code>yarn add react-dropzone</code>
- Or: <code>npm install react-dropzone</code>

<h2>Create Page for Upload Files</h2>
Let's create a File Upload UI with Progress Bar, Card, Button and Message.

First we create a React template with React Hooks (<code>useState</code>, <code>useEffect</code>) and import <code>react-dropzone</code>, <code>FileUploadService</code> functions:

<strong>components</strong>/<em>FileUpload.js</em>
```js
import React, { useState, useEffect } from "react";
import Dropzone from "react-dropzone";
import { getFiles, uploadFile } from "../services/FileUploadService";

const UploadFiles = () => {
  
  return (
    
  );
};

export default UploadFiles;
```

Then we define the state using React Hooks:

```js
const UploadFiles = () => {

  const [selectedFiles, setSelectedFiles] = useState(undefined);
  const [currentFile, setCurrentFile] = useState(undefined);
  const [progress, setProgress] = useState(0);
  const [message, setMessage] = useState("");

  const [fileInfos, setFileInfos] = useState([]);

  ...
}
```

Next we define <code>onDrop()</code> method which helps us to get the selected Files from <code><Dropzone></code> element later.
```js
const UploadFiles = () => {
  ...
  const onDrop = (files) => {
    if (files.length > 0) {
      setSelectedFiles(files);
    }
  };

  ...
}
```

We use <code>selectedFiles</code> for accessing current File as the first Item. Then we call <code>UploadService.uploadFile()</code> method on the <code>currentFile</code> with a callback. So create following <code>upload()</code> method:

```js
const UploadFiles = () => {
  ...
  const upload = () => {
    let currentFile = selectedFiles[0];

    setProgress(0);
    setCurrentFile(currentFile);

    uploadFile(currentFile, (event) => {
      setProgress(Math.round((100 * event.loaded) / event.total));
    })
      .then((response) => {
        setMessage(response.data.message);
        return getFiles();
      })
      .then((files) => {
        setFileInfos(files.data);
      })
      .catch(() => {
        setProgress(0);
        setMessage("Could not upload the file!");
        setCurrentFile(undefined);
      });

    setSelectedFiles(undefined);
  };
  ...
}
```

The progress will be calculated basing on <code>event.loaded</code> and <code>event.total</code>.
If the transmission is done, we call <code>UploadService.getFiles()</code> to get the files' information and assign the result to <code>fileInfos</code> state, which is an array of <code>{name, url}</code> objects.

We also need to do this work in the Effect Hook <code>useEffect()</code> method which serves the same purpose as <code>componentDidMount()</code>:
```js
const UploadFiles = () => {
  ...
  useEffect(() => {
    getFiles().then((response) => {
      setFileInfos(response.data);
    });
  }, []);

  ...
}
```

Now we return the Upload File UI. Add the following code inside <code>return()</code> block:
```jsx
const UploadFiles = () => {
  ...
  return (
    <div>
      {currentFile && (
        <div className="progress mb-3">
          <div
            className="progress-bar progress-bar-info progress-bar-striped"
            role="progressbar"
            aria-valuenow={progress}
            aria-valuemin="0"
            aria-valuemax="100"
            style={{ width: progress + "%" }}
          >
            {progress}%
          </div>
        </div>
      )}

      <Dropzone onDrop={onDrop} multiple={false}>
        {({ getRootProps, getInputProps }) => (
          <section>
            <div {...getRootProps({ className: "dropzone" })}>
              <input {...getInputProps()} />
              {selectedFiles && selectedFiles[0].name ? (
                <div className="selected-file">
                  {selectedFiles && selectedFiles[0].name}
                </div>
              ) : (
                "Drag and drop file here, or click to select file"
              )}
            </div>
            <aside className="selected-file-wrapper">
              <button
                className="btn btn-success"
                disabled={!selectedFiles}
                onClick={upload}
              >
                Upload
              </button>
            </aside>
          </section>
        )}
      </Dropzone>

      <div className="alert alert-light" role="alert">
        {message}
      </div>

      {fileInfos.length > 0 && (
        <div className="card">
          <div className="card-header">List of Files</div>
          <ul className="list-group list-group-flush">
            {fileInfos.map((file, index) => (
              <li className="list-group-item" key={index}>
                <a href={file.url}>{file.name}</a>
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
};
```

In the code above, we use <a href="https://getbootstrap.com/docs/4.4/components/progress/">Bootstrap Progress Bar</a>:
<ul>
	<li><code>.progress</code> as a wrapper</li>
	<li>inner <code>.progress-bar</code> to indicate the progress</li>
	<li><code>.progress-bar</code> requires <code>style</code> to set the width by percentage</li>
	<li><code>.progress-bar</code> also requires <code>role</code> and some aria attributes to make it accessible</li>
	<li>label of the progress bar is the text within it</li>
</ul>

To display List of uploaded files, we iterate over <code>fileInfos</code> array using <code>map()</code> function. On each file item, we use <code>file.url</code> as <code>href</code> attribute and <code>file.name</code> for showing text.

Don't forget to export the function component:
```jsx
const UploadFiles = () => {
  ...
}

export default UploadFiles;
```

<h2>CSS style for Dropzone and File</h2>
Open <em>App.css</em> and add following styles:

```css
.dropzone {
  text-align: center;
  padding: 30px;
  border: 3px dashed #eeeeee;
  background-color: #fafafa;
  color: #bdbdbd;
  cursor: pointer;
  margin-bottom: 20px;
}

.selected-file-wrapper {
  text-align: center;
}

.selected-file {
  color: #000; 
  font-weight: bold;
}
```

<h2>Add Drag and Drop File Upload Component to App Component</h2>
Open <em>App.js</em>, import and embed the <code>UploadFiles</code> Component tag.

```jsx
import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";

import FileUpload from "./components/FileUpload";

function App() {
  return (
    <div className="container" style={{ width: "600px" }}>
      <div className="my-3">
        <h3>bezkoder.com</h3>
        <h4>React Drag & Drop File Upload</h4>
      </div>

      <FileUpload />
    </div>
  );
}

export default App;
```

<h2>Configure Port for React App</h2>
Because most of HTTP Server use CORS configuration that accepts resource sharing restricted to some sites or ports, you need to configure port for our App.

In project folder, create <em>.env</em> file with following content:
```
PORT=8081
```

So our app will run at port 8081.

<h2>Run the App</h2>
You can find how to implement the Rest APIs Server at one of following posts:
- <a href="https://bezkoder.com/node-js-express-file-upload/">Node.js Express File Upload Rest API example</a>
- <a href="https://www.bezkoder.com/node-js-upload-store-images-mongodb/">Node.js Express File Upload to MongoDB example</a>
- <a href="https://bezkoder.com/google-cloud-storage-nodejs-upload-file/">Node.js Express File Upload to Google Cloud Storage example</a>
- <a href="https://bezkoder.com/spring-boot-file-upload/">Spring Boot Multipart File upload (to static folder) example</a>

Run this React Client with command: <code>yarn start</code> or <code>npm run start</code>.

Open Browser with url <code>http://localhost:8081/</code> and check the result.

<h2>Conclusion</h2>
Today we're learned how to build a React Hooks application for Drap and Drop file upload using React-Dropzone, Axios, Bootstrap with Progress Bar. We also provide the ability to show list of files, upload progress percentage, and to download file from the server.

For Multiple Files Upload like this:

<img src="https://bezkoder.com/wp-content/uploads/2021/02/react-hooks-multiple-files-upload-example.png" alt="react-hooks-multiple-files-upload-example" width="600" height="650" class="alignnone size-full wp-image-6744" />

Please visit:
<a href="https://bezkoder.com/react-hooks-multiple-file-upload/">React Hooks Multiple File upload example with Axios & Progress Bar</a>

Using React Components instead:
<a href="https://www.bezkoder.com/react-drag-drop-file-upload/">React Drag and Drop File Upload example</a>

Happy Learning! See you again.

<h2>Further Reading</h2>
<ul>
	<li><a href="https://github.com/axios/axios">https://github.com/axios/axios</a></li>
	<li><a href="https://reactjs.org/docs/hooks-intro.html">React Hooks</a></li>
	<li><a href="https://www.bezkoder.com/react-custom-hook/">React Custom Hook</a></li>
	<li><a href="https://getbootstrap.com/docs/4.4/components/progress/">Bootstrap 4 Progress</a></li>
	<li><a href="https://www.npmjs.com/package/react-dropzone">react-dropzone</a></li>
</ul>

More Practice:
- <a href="https://www.bezkoder.com/react-custom-hook/">React Custom Hook</a>
- <a href="https://bezkoder.com/react-file-upload-spring-boot/">React File Upload/Download example with Spring Boot Rest Api</a>
- <a href="https://bezkoder.com/react-hooks-crud-axios-api/">React Hooks CRUD example with Axios and Web API</a>
- <a href="https://bezkoder.com/react-form-validation-hooks/">React Form Validation with Hooks example</a>
- <a href="https://bezkoder.com/react-hooks-jwt-auth/">React Hooks: JWT Authentication (without Redux) example</a>
- <a href="https://bezkoder.com/react-hooks-redux-login-registration-example/">React + Redux: JWT Authentication example</a>

Fullstack:
- <a href="https://bezkoder.com/react-spring-boot-crud/">React + Spring Boot + MySQL: CRUD example</a>
- <a href="https://bezkoder.com/spring-boot-react-postgresql/">React + Spring Boot + PostgreSQL: CRUD example</a>
- <a href="https://bezkoder.com/react-spring-boot-mongodb/">React + Spring Boot + MongoDB: CRUD example</a>
- <a href="https://bezkoder.com/react-node-express-mysql/">React + Node.js + Express + MySQL: CRUD example</a>
- <a href="https://bezkoder.com/react-node-express-postgresql/">React + Node.js + Express + PostgreSQL example</a>
- <a href="https://bezkoder.com/react-redux-mysql-crud/">React Redux + Node.js + Express + MySQL: CRUD example</a>
- <a href="https://bezkoder.com/react-node-express-mongodb-mern-stack/">React + Node.js + Express + MongoDB example</a>
- <a href="https://bezkoder.com/django-react-hooks/">React + Django + Rest Framework example</a>

Serverless:
- <a href="https://bezkoder.com/react-firebase-hooks-crud/">React Hooks + Firebase Realtime Database: CRUD App</a>
- <a href="https://bezkoder.com/react-hooks-firestore/">React Hooks + Firestore example: CRUD app</a>

<h2>Source Code</h2>
You can find the complete source code for this tutorial at <a href="https://github.com/bezkoder/drag-drop-file-upload-react-hooks">Github</a>.