Tutorial

How To Create Drag and Drop File Uploads in React with react-dropzone

React

Introduction

react-dropzone is an HTML5-compliant React component for handling the dragging and dropping of files.

HTML5 supports file uploads with <input type="file" />. react-dropzone provides you with additional functionality like customizing the dropzone, displaying a preview, and restricting file types and amounts.

Note: If you are working with Vue instead of React, consult our tutorial for vue-dropzone.

In this tutorial, you will learn about adding react-dropzone to your React projects and explore some of the functionality it provides.

Prerequisites

To complete this tutorial, you’ll need:

This tutorial was verified with Node v15.3.0, npm v7.4.0, react v17.0.1, and react-dropzone v11.2.4.

Step 1 — Setting Up the Project

Start with using create-react-app to generate a React App and then install dependecies:

  • npx create-react-app react-dropzone-example

Change into the new project directory:

  • cd react-dropzone-example

Install react-dropzone:

  • npm install react-dropzone@11.2.4

At this point, you have a new React project with react-dropzone.

Step 2 — Adding the Dropzone Component

react-dropzone has default settings that allow you to add it with minimal configuration.

At a minimum, you will need an onDrop property that will handle the dropped files and some call-to-action text to help limit any user confusion:

src/DropzoneComponent.js
import React, { useCallback } from 'react';
import { useDropzone } from 'react-dropzone';

function DropzoneComponent() {
  const onDrop = useCallback(acceptedFiles => {
    console.log(acceptedFiles);
  }, []);

  const {
    getRootProps,
    getInputProps
  } = useDropzone({
    onDrop
  });

  return (
    <div {...getRootProps()}>
      <input {...getInputProps()} />
      <div>Drag and drop your images here.</div>
    </div>
  )
}

export default DropzoneComponent;

This code provides drag and drop functionality for your application.

Note: It is worth noting that even though react-dropzone is designed to drag and drop files, it does accept click events to the dropzone by default which will launch a dialog for file selection.

Add the component to your React application:

src/App.js
import DropzoneComponent from './DropzoneComponent';

function App() {
  return (
    <div className="App">
      <DropzoneComponent />
    </div>
  );
}

export default App;

Run your application and observe it in a web browser. You should see a div with the text: Drag and drop your images here.

Experiment with dragging and dropping various files to the React Dropzone component. The code currently uses a console.log to display the files. The information from uploaded files includes name, lastModified, size, and type.

At this point, you have a working React Dropzone component with the default configuration. The react-dropzone documentation additional configuration options.

Step 3 — Styling the Dropzone Component

By default, the react-dropzone will have no styles. The documentation provides styles for a common appearance that uses a combination of flexbox and dashed borders to indicate to users an area to drag and drop files.

react-dropzone also supports props for when the component is actively interacted with (isDragActive), accepted the file (isDragAccept), or rejected the file (isDragReject).

Revisit your DropzoneComponent and modify it to use isDragActive, isDragAccept, and isDragReject when applied to JPEG and PNG image file types:

src/DropzoneComponent.js
import React, { useCallback, useMemo } from 'react';
import { useDropzone } from 'react-dropzone';

const baseStyle = {
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  padding: '20px',
  borderWidth: 2,
  borderRadius: 2,
  borderColor: '#eeeeee',
  borderStyle: 'dashed',
  backgroundColor: '#fafafa',
  color: '#bdbdbd',
  transition: 'border .3s ease-in-out'
};

const activeStyle = {
  borderColor: '#2196f3'
};

const acceptStyle = {
  borderColor: '#00e676'
};

const rejectStyle = {
  borderColor: '#ff1744'
};

function DropzoneComponent(props) {
  const onDrop = useCallback(acceptedFiles => {
    console.log(acceptedFiles);
  }, []);

  const {
    getRootProps,
    getInputProps,
    isDragActive,
    isDragAccept,
    isDragReject
  } = useDropzone({
    onDrop,
    accept: 'image/jpeg, image/png'
  });

  const style = useMemo(() => ({
    ...baseStyle,
    ...(isDragActive ? activeStyle : {}),
    ...(isDragAccept ? acceptStyle : {}),
    ...(isDragReject ? rejectStyle : {})
  }), [
    isDragActive,
    isDragReject,
    isDragAccept
  ]);

  return (
    <div {...getRootProps({style})}>
      <input {...getInputProps()} />
      <div>Drag and drop your images here.</div>
    </div>
  )
}

export default DropzoneComponent;

This code will produce the following result:

Screenshot of React Dropzone with custom styles

Changing the appearance of the component for acceptions and rejections can help provide feedback to the user on whether or not their file is valid.

Step 4 — Adding Image Previews

A preview is a copy of the image that is dragged and dropped in the component. This is helpful to provide visual feedback to the user to verify the contents of the image file they selected.

Previews were removed in version 7.0.0, however, the documentation provides an alternative for readding it with a combination of Object.assign() and URL.createObjectURL().

Revisit your DropzoneComponent and modify it to use preview:

src/DropzoneComponent.js
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDropzone } from 'react-dropzone';

const baseStyle = {
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  padding: '20px',
  borderWidth: 2,
  borderRadius: 2,
  borderColor: '#eeeeee',
  borderStyle: 'dashed',
  backgroundColor: '#fafafa',
  color: '#bdbdbd',
  transition: 'border .3s ease-in-out'
};

const activeStyle = {
  borderColor: '#2196f3'
};

const acceptStyle = {
  borderColor: '#00e676'
};

const rejectStyle = {
  borderColor: '#ff1744'
};

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

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

  const {
    getRootProps,
    getInputProps,
    isDragActive,
    isDragAccept,
    isDragReject
  } = useDropzone({
    onDrop,
    accept: 'image/jpeg, image/png'
  });

  const style = useMemo(() => ({
    ...baseStyle,
    ...(isDragActive ? activeStyle : {}),
    ...(isDragAccept ? acceptStyle : {}),
    ...(isDragReject ? rejectStyle : {})
  }), [
    isDragActive,
    isDragReject,
    isDragAccept
  ]);

  const thumbs = files.map(file => (
    <div key={file.name}>
      <img
        src={file.preview}
        alt={file.name}
      />
    </div>
  ));

  // clean up
  useEffect(() => () => {
    files.forEach(file => URL.revokeObjectURL(file.preview));
  }, [files]);

  return (
    <section>
      <div {...getRootProps({style})}>
        <input {...getInputProps()} />
        <div>Drag and drop your images here.</div>
      </div>
      <aside>
        {thumbs}
      </aside>
    </section>
  )
}

export default DropzoneComponent;

Note: To avoid memory leaks, you will need to call URL.revokeObjectURL(file.preview) to avoid storing the preview unnecessarily.

Now whenever a file (or files) has been dropped, the file will be appended to the state and a preview will be displayed.

Conclusion

In this tutorial, you were introduced to react-dropzone and how it could be used in a React application to provide advanced drag and drop functionality to file uploads.

If you’d like to learn more about React, take a look at our How To Code in React.js series, or check out our React topic page for exercises and programming projects.

Creative Commons License