Drag and Drop Image uploader using React-dropzone and Cloudinary
Tags: webdev, react, cloudinary, tutorial
Published: 2023-01-01 19:29:38

Media uploading has transitioned from conventional clicking and uploading to drag-and-drop functionality. Modern applications have adopted this new trend for a better user experience. In this tutorial, we will be implementing a drag-and-drop functionality in a react application. 

To achieve that, we'll use the following technologies:

**React-dropzone**
[react-dropzone](https://react-dropzone.js.org) is a set of React libraries that help developers to build complex drag-and-drop functionalities while keeping their components destructured. In other words, it is a React hook that makes it easy to construct drag-and-drop interfaces quickly rather than from scratch.

**Cloudinary**
[cloudinary](http://cloudinary.com) provides cloud-based image and video management services. It enables users to upload, store, manage, manipulate, and deliver photos and videos for websites and apps.

**React**
[react](https://reactjs.org/docs/getting-started.html) is a free and open-source front-end JavaScript library for building user interfaces based on UI components. Facebook creates it.

**What we will build**
This series will focus on building a drag-and-drop zone for files and then uploading the dropped file(s) to a server. We'll use react-dropzone to implement the drag-and-drop functionalities, Afterwards, We’ll upload the files directly from the browser using Cloudinary Unsigned Upload.

Here's a demo of the end product in action:

Demo:
![demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/drlcdg5mb7wjh80y9pc9.gif)
**Prerequisites**
- To follow this project, some experience with React.js and a cloudinary is required.
- Node.js installed on the computer for local development.



**Getting Started with React application**
First, let’s run the following terminal command to create the react.js application in a folder called file_uploads:

```javascript
$ npx create-react-app file_uploads
```
Next, navigate into the application directory and install the dropzone npm package with the following commands:

```javascript
$ cd file_uploads // to navigate into the project directory
$ npm install react-dropzone // to install the dropzone library
```
Now, let’s start the application with the following command:

```javascript
$ npm start
```
React.js will start a live development server on http://localhost:3000.



**Building the application**
We want to create an area where, when we can click on it, the React-dropzone library will initiate a file selection dialog, allowing us to select and upload files.

The easiest way to get started with React Dropzone is to use the `useDropzone` hook. The `useDropzone` returns `getRootProps`, `getInputProps`, and `isDragActive`, which we can call to get the Drag and Drop API properties and pass those to our elements. The root element can be any element we want. For this example, we will use a `<div>`. The input element must be an `<input>`. 

Next, let's create a component folder in the root directory of the project and create a `Dropzone.js` file with the following snippets:
```javascript
//Dropzone.js
import React from 'react';
import { useDropzone } from 'react-dropzone';

function Dropzone() {
  const {
    getRootProps,
    getInputProps,
    isDragActive
  } = useDropzone({
    onDrop
  });
  return (
    ...
  );
}
export default Dropzone;
```
Here, we imported `useDropzone` from 'react-dropzone' and destructured `getRootProps`, `getInputProps` and `isDragActive` from it.

Next, in the App.js, let's import and render the `Dropzone.js` component as shown below:
```javascript
import React from 'react';
import Dropzone from './Dropzone';
function App() {
  return (
    <div>
      <Dropzone />
    </div>
  );
}

export default App;
```
Next, in the return function of the Dropzone.js component, let’s return a div and input elements and pass getRootProps() and getInputProps() to them respectively. Also, we'll use the isDragActive to render the input label conditionally, like in the below:
```javascript
//component/Dropzone.js
<div {...getRootProps()}>
  <input {...getInputProps()} />
 {isDragActive ? (
           <p className="dropzone">Drop your image files here</p>
          ) : (
           <p className="dropzone">
     Drag and drop some files here, or click to select files</p>
          )}  
</div>
```
The `getRootProps()` — is a function that returns an object of the properties needed on the root element. It sets drag and drops API properties on the chosen root.

The properties and methods look like this:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o421snetzsstcadzpx0m.png)
These events allow the browser to monitor the users' drag and drop engagements.


The `getInputProps()` — is a function that returns an object of the properties needed on the input element.

Here are the properties and methods:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6edttpz84qa1ojxfyeux.png)
The `isDragActive()` — is a boolean, true if a file is dragged over the dropzone area and not included in acceptable file types but false if not. 


**Handling Drop Functionality**
We can drag and drop files into our component, but we are not doing anything with these files yet. 

The `useDropzone` hook takes an onDrop function as props to complete the drag and drop functionality and a config object as an optional parameter.

The onDrop property takes a callback function and calls it when the drop event occurs. 

Now, let's create the onDrop function to preview the copy of the image being dragged and dropped into the dropzone and pass it to the useDropzone hook like in the below:
```javascript
import React, { useState, useCallback, useEffect } from "react";
//other imports here

function Dropzone() {
  const [files, setFiles] = useState([]);

  const onDrop = useCallback((acceptedFiles) => {
    setFiles(
      acceptedFiles.map((file) =>
        Object.assign(file, {
          preview: URL.createObjectURL(file)
        })
      )
    );
  }, []);

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: "images/*"
  });

//return (); functiion here
}
export default Dropzone;
```
In the snippets above, we:

- Created `files` state constant with the `useState` hook to store the file object.
- Wrapped the function with the `useCallback` hook to avoid triggering unnecessary re-renders.
- Updated the `files` state by looping through the `acceptedFiles` parameter and calling `Object.assign()` and `URL.createObjectURL()` functions.
- Passed the `onDrop()` function to the `useDropzone` hook and also gave the `accept` property and set it to a string ("images/*") to accept all image files.

Next, create a `thumbs()` function that will loop through the file's state constant and display a preview using the image tag. Update the `Dropzone.js` file with the following snippets:
```javascript
//component/Dropzone.js
//imports here
function Dropzone() {
 //state constant here

//onDrop() function here

 //the Dropzone implementation here
  useEffect(() => {
    return () => files.forEach((file) => URL.revokeObjectURL(file.preview));
  }, [files]);

  const thumbs = files.map((file) => (
    <div key={file.name}>
      <div>
        <img
          src={file.preview}
          onLoad={() => {
            URL.revokeObjectURL(file.preview);
          }}
          alt=""
        />
      </div>
    </div>
  ));
  return (
    <div className="App">
      <div>
        <div {...getRootProps()}>
          //input and p tags here
        </div>
      </div>
      
      <aside>{thumbs}</aside>
    </div>
  );
}
export default Dropzone;
```
Here, we created the `thumbs()` function, rendered it in the `return()` function, and used the `useEffect` hook to prevent memory leaks and avoid unnecessarily storing the preview.

Now when we drop a file (or files), the file will be appended to the state and displayed in a preview.

**Upload to cloudinary with unsigned upload**
To upload the file to Cloudinary, [log in](https://cloudinary.com/users/login) or create a free account [here](https://cloudinary.com/users/register/free). From the Dashboard, click on the Settings icon and navigate to uploads.

![CloudImage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5tusspjwe3qsfrxdecw.png)
Next, scroll down to upload presets, add an unsigned preset, choose a folder to upload the files, and select a name for the preset.

![CloudImage2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzpxhwb4xagbblmqlcs4.png)
Learn more about [Cloudinary unsigned uploads](https://cloudinary.com/documentation/upload_images#unsigned_upload).


Next, let's create a handleUpload() to upload the file to Cloudinary and render a button that will trigger the handleUpload() in the return(). Update the Dropzone.js file with the following snippets:
```javascript
//component/Dropzone.js
//imports here
function Dropzone() {
  const [files, setFiles] = useState([]);

// onDrop() function here

  // useDropzone hook here

 // thumbs() function here

 //useEffect hook here

  const handleUpload = () => {
    const url = "https://api.cloudinary.com/v1_1/johnpaul/image/upload";
    const formData = new FormData();
    let file = files[0];
    formData.append("file", file);
    formData.append("upload_preset", "dryqolej");
    fetch(url, {
      method: "POST",
      body: formData
    })
      .then((response) => {
        return response.json();
      })
      .then((data) => {
        console.log(data);
      });
  };

  return (
    <div className="App">
      <div>
        <div {...getRootProps()}>
    // input and p tags here
        </div>
      </div>
      {files.length > 0 && (
        <button className="button" onClick={handleUpload}>
          Upload
        </button>
      )}
      <aside>{thumbs}</aside>
    </div>
  );
}
export default Dropzone;
```
In the snippets above, we created the `handleUpload()` function and conditionally rendered the Upload button if there is a file in the files state. Then passed the `handleUpload()` to the `onClick` event of the button.

Now, when we drag and drop a file in the drop area, we'll see a file preview. If we click the Upload button and go over to our console or our media library, we will see the image object Uploaded to Cloudinary.

The final result will look like the GIF shown below.

![demo3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9kt0rt8473k40a5o6f16.gif)

**Conclusion**

In this tutorial, we explored react-dropzone and how it can be integrated into a React application to offer advanced drag and drop functionalities to media uploads. Implementing such a fantastic user experience feature in our React application should now be a thing of the past. I hope this tutorial is helpful. The complete source code for this tutorial is available on [Github
](https://github.com/JohnpaulEze/file-upload).


**Resources**
These resources might be useful 
- [React-dropzone documentation](https://react-dropzone.js.org/)

- [Cloudinary Unsigned Upload](https://cloudinary.com/documentation/upload_images#unsigned_upload)