DEV Community

Cover image for Create a Drag-and-Drop Zone in React with react-dropzone
OpenReplay Tech Blog
OpenReplay Tech Blog

Posted on • Originally published at blog.openreplay.com

Create a Drag-and-Drop Zone in React with react-dropzone

by Ejiro Thankgod

Drag and drop is a platform whereby an application uses drag-and-drop features on browsers. The user selects a draggable item with a mouse or touchpad, drags those files to a droppable element (drop zone), and drops them by releasing the mouse button.

React-dropzone is a set of React libraries to help you build complex drag-and-drop interfaces while keeping your components destructured. The most common use cases for drag-and-drop in React comprise uploading files, rearranging images/files, and moving files between multiple folders. We’ll create a simple React application with a drag-and-drop interface, just like the one below.

a React drag-and-drop interface

By the end of this tutorial, in our React application we’ll be able to do the following:

  • Drag-and-drop a file on the drag-and-drop zone,
  • Click event which initiates file selection dialog
  • Display file preview on the drop zone,
  • List file name, file type, and file size,
  • Alternative option ‘click to open’ button,
  • Indicate the file received on the drop zone,

Getting Started

First of all, we are going to create our React app. I believe we already know this process, but we will start from scratch here, for starters. We are creating our React app from our terminal using npx create-react-app drag-n-drop for those using npm, while others using yarn should run yarn create React-app drag-n-drop.

After, run npm start or yarn start to begin. You can manually delete some of the generated boilerplate files and code, such as:

  • favicon.ico
  • all logo files (logo192.png, logo512.png, logo.svg)
  • manifest.json
  • robots.txt
  • reportWebVitals.js
  • setupTests.js
  • App.test.js

Alternatively, you could run npx clean-React-app to remove such files.

How to Implement a Drag and Drop Zone in React

We won’t be building all the logic and components from scratch. Instead, we’ll use one of the most common React drag-and-drop libraries available: React-dropzone. React-dropzone is a very powerful library and custom component. It’s a simple React hook to create an HTML-5 compliant drag-and-drop zone for files. Dropzone provides additional functions such as customizing the dropzone, restricting file types, etc. If you need a visual explanation of a drag-and-drop with React-dropzone, you can watch this video.

We will install our React dropzone with npm install React-dropzone or yarn add React-dropzone. Right now, our code should be looking something like this in our App.js component.

import React from "react";

function App() {
  return <div></div>;
}

export default App;
Enter fullscreen mode Exit fullscreen mode

We want to create an area where, when we click on it, the React-dropzone library initiates the file selection dialog, allowing us to select and upload files. We’ll start by creating a component.

//*Dropzone.js*//

import React from "React";
import { useDropzone } from "React-dropzone";

function Dropzone({ open }) {
  const { getRootProps, getInputProps } = useDropzone({});
  return (
    <div {...getRootProps({ className: "dropzone" })}>
      <input className="input-zone" {...getInputProps()} />
      <div className="text-center">
        <p className="dropzone-content">
          Dragndrop some files here, or click to select files
        </p>
        )}
      </div>
    </div>
  );
}

export default Dropzone;
Enter fullscreen mode Exit fullscreen mode

Now we have to import the Dropzone to our App.js ;

//*App.js*//

import React from "React";
import Dropzone from "./Dropzone";

function App() {
  // const [images, setImages] = useState([]);
  return (
    <div>
      <div className="container">
        <h1 className="text-center">Drag and Drop Test</h1>
        <Dropzone />
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now that we have done that, our browser should look like this.

our drag-and-drop app

Styling

Although our React-dropzone is working well, it doesn’t look pleasant, so we will style it a little bit so that it may look more like an actual dropzone. Now we move to index.css to implement some styling.

//*index.css*//
body {
  text-align: center;
  padding: 20px;
  border: 3px blue dashed;
  width: 60%;
  margin: auto;
}
Enter fullscreen mode Exit fullscreen mode

We must import index.css in our App.js. The results are as follows.

basic css styling

As you can see, this is our React-dropzone. However, it still needs more styling. I want to add some components to it to make it look more functional because, right now, the only thing it can do is the drag-and-drop and the click event, which initiate the file selection dialog. We also need to have a path to store files drawn into the dropzone.

Accepting Files and Recording File Properties

If you’re following this guide, you will have noticed that the React-dropzone does not accept files nor list the file name, file type, and file size; that’s what we’ll be working on. We have to create a function for our dropzone to enable it to accept files and display file names, types, and sizes. Our code should look like this.

import React from "React";
import { useDropzone } from "React-dropzone";
import "./index.css";

function Dropzone({ open }) {
  const { getRootProps, getInputProps, acceptedFiles } =
    useDropzone({});

  const files = acceptedFiles.map((file) => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  ));

  return (
    <div className="container">
      <div {...getRootProps({ className: "dropzone" })}>
        <input {...getInputProps()} />
        <p>Drag 'n' drop some files here</p>
      </div>
      <aside>
        <ul>{files}</ul>
      </aside>
    </div>
  );
}

export default Dropzone;
Enter fullscreen mode Exit fullscreen mode

With our app looking like this;

updated code

When we drag a file into our dropzone, the file is accepted, and its properties (name, type, and size) are displayed.

Adding our Buttons and Styling them

Our React-dropzone is getting into shape, but we would like to manually add a button to initiate the file selection dialog. All we have to do is create a button component beneath our <p> in our Dropzone.js.

...
<p className="dropzone-content">
  Drag’ n’ drop some files here, or click to select files
</p>
)}
<button type="button" onClick={open} className="btn">
Click to select files
</button>
...
Enter fullscreen mode Exit fullscreen mode

All we have to do now is style our button with the class name btn and make it look beautiful. We’ll go to our index.css and beautify our buttons.

.btn {
  border: none;
  text-align: center;
  background-color: rgb(218, 216, 216);
  height: 50px;
  border-radius: 12px;
  color: black;
  font-weight: bold;
  transition-duration: 0.6s;
}

.btn:hover {
  background-color: blue;
  color: aliceblue;
}
Enter fullscreen mode Exit fullscreen mode

better styling applied

And it looks nice! Up next, we’ll be working on the isDragActive component. It is set to detect if a draggable file is within the dropzone; then the text at the dropzone changes from ‘drag'n'drop some files here or click to select’ to ‘release to drop the file here’.

Implementing the isDragActive Components

We'll need some code.

//*Dropzone.js*//

...

function Dropzone({ open }) {
  const { getRootProps, getInputProps, isDragActive, acceptedFiles } =
    useDropzone({});

  ...

  return (
    <div {...getRootProps({ className: "dropzone" })}>
      <input className="input-zone" {...getInputProps()} />
      <div className="text-center">
        {isDragActive ? (
          <p className="dropzone-content">
            Release to drop the files here
          </p>
        ) : (
          <p className="dropzone-content">
            Dragndrop some files here, or click to select files
          </p>
        )}
        <button type="button" onClick={open} className="btn">
          Click to select files
        </button>
      </div>
      <aside>
        <ul>{files}</ul>
      </aside>
    </div>
  );
}

export default Dropzone;
Enter fullscreen mode Exit fullscreen mode

Open Source Session Replay

OpenReplay is an open-source alternative to FullStory and LogRocket. It gives you full observability by replaying everything your users do on your app and showing how your stack behaves for every issue. OpenReplay is self-hosted for full control over your data.

replayer.png

Happy debugging, for modern frontend teams - start monitoring your web app for free.

Display Image Preview

To show images in grid layout (another cool feature) we’ll create a component called imagegrid.js. We will use another library called React-dnd.

//*ImageGrid.js*//

...

function App() {
  const [images, setImages] = useState([]);
  const onDrop = useCallback((acceptedFiles) => {
    acceptedFiles.map((file) => {
      const reader = new FileReader();

      reader.onload = function (e) {
        setImages((prevState) => [
          ...prevState,
          { id: cuid(), src: e.target.result },
        ]);
      };

      reader.readAsDataURL(file);
      return file;
    });
  }, []);

  return (
    <main className="App">
      <h1 className="text-center">Drag and Drop Test</h1>
      <Dropzone onDrop={onDrop} accept={"image/*"} />
      <ImageGrid images={images} />
    </main>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

If you check <Dropzone onDrop={onDrop} accept={“image/*”} />, you will see that we made it to only accept images, but you can allow multiple file types. We will also apply some styling to it.

//*index.css*//

.file-list {
  display: flex;
  flex-wrap: wrap;
  width: 65%;
  margin: 20px auto;
  padding: 10px;
  border: 3px dotted black;
}

.file-list img {
  height: 300px;
  width: 300px;
  object-fit: cover;
}
Enter fullscreen mode Exit fullscreen mode

the final app showing previews of images

And our drag-and-drop zone is working perfectly with our well-styled button for manually initiating file dialog and our display preview on our images.

Conclusion

There are different React drag-and-drop libraries ranging from React-beautiful-dnd down to React-grid-layout and, finally, React-dnd (which I used for our image preview, and I’ll be discussing it in my next article). Although they have their pros and cons, they all work uniquely depending on what you use them for. For example, we used React-dropzone to customize our drag-and-drop zone earlier, and it looked winsome and responsive. There are so many ways you could customize your React-dropzone. Maybe you want a specific file type as we did there, or you want an accept-or-reject list? You can also implement that and open the file dialog programmatically with a button. You could also check their website for more information.

For reference, here’s the complete code for the project here.

//*App.js*//

import React, { useCallback, useState } from "React";
import cuid from "cuid";
import Dropzone from "./Dropzone";
import ImageGrid from "./components/ImageGrid";
import "./index.css";
import "./App.css";

function App() {
  const [images, setImages] = useState([]);
  const onDrop = useCallback((acceptedFiles) => {
    acceptedFiles.map((file) => {
      const reader = new FileReader();

      reader.onload = function (e) {
        setImages((prevState) => [
          ...prevState,
          { id: cuid(), src: e.target.result },
        ]);
      };

      reader.readAsDataURL(file);
      return file;
    });
  }, []);

  return (
    <main className="App">
      <h1 className="text-center">Drag and Drop Test</h1>
      <Dropzone onDrop={onDrop} accept={"image/*"} />
      <ImageGrid images={images} />
    </main>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode
//*ImageGrid.js*//

import React from "React";
// Rendering individual images
const Image = ({ image }) => {
  return (
    <div className="file-item">
      <img
        alt={`img - ${image.id}`}
        src={image.src}
        className="file-img"
      />
    </div>
  );
};

// ImageList Component//
const ImageGride = ({ images }) => {
  // render each image by calling Image component
  const renderImage = (image, index) => {
    return <Image image={image} key={`${image.id}-image`} />;
  };
  // Return the list of files//
  return (
    <section className="file-list">{images.map(renderImage)}</section>
  );
};

export default ImageGride;
Enter fullscreen mode Exit fullscreen mode
//*Dropzone.js*//

import React from "React";
import { useDropzone } from "React-dropzone";

function Dropzone({ onDrop, accept, open }) {
  const { getRootProps, getInputProps, isDragActive, acceptedFiles } =
    useDropzone({
      accept,
      onDrop,
    });

  const files = acceptedFiles.map((file) => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  ));

  return (
    <div>
      <div {...getRootProps({ className: "dropzone" })}>
        <input className="input-zone" {...getInputProps()} />
        <div className="text-center">
          {isDragActive ? (
            <p className="dropzone-content">
              Release to drop the files here
            </p>
          ) : (
            <p className="dropzone-content">
              Drag n drop some files here, or click to select files
            </p>
          )}
          <button type="button" onClick={open} className="btn">
            Click to select files
          </button>
        </div>
      </div>
      re
      <aside>
        <ul>{files}</ul>
      </aside>
    </div>
  );
}

export default Dropzone;
Enter fullscreen mode Exit fullscreen mode
//*index.css*//
body {
  text-align: center;
}

.dropzone {
  text-align: center;
  padding: 20px;
  border: 3px blue dashed;
  width: 60%;
  margin: auto;
}

.btn {
  border: none;
  text-align: center;
  background-color: rgb(218, 216, 216);
  height: 50px;
  border-radius: 12px;
  color: black;
  font-weight: bold;
  transition-duration: 0.6s;
}

.btn:hover {
  background-color: blue;
  color: aliceblue;
}

.file-list {
  /* border: 3px dotted black; */
  display: flex !important;
  flex-wrap: wrap;
  width: auto;
  padding: 10px 20px;
  margin: 20px 30px;
  /* border: 3px dotted black; */
}

.file-list img {
  height: 100%;
  width: 100px;
  padding-right: 10px;
  object-fit: cover;
}
Enter fullscreen mode Exit fullscreen mode

We’ve successfully built a small project for dragging and dropping files. There are many more functionalities in React. For more, check out the links below.

Happy coding!

Top comments (0)