Programmable Media

Drag-and-drop uploads in React (video tutorial)

Last updated: Nov-13-2024

Overview

Learn how to implement drag-and-drop uploads to Cloudinary in your React app using the react-dropzone library.

Video tutorial


View the code
You can find the code from this tutorial in GitHub.

Tutorial contents

This tutorial presents the following topics. Click a timestamp to jump to that part of the video.

Introduction to file upload in React

Jump to this spot in the video  0:12 The traditional way to select files on websites is using a file picker input element. However, to achieve drag-and-drop functionality, you can use the browser's drag-and-drop API or a library like react-dropzone, which simplifies this process. This tutorial demonstrates how to set up react-dropzone, import its component, and use the useDropzone hook with an onDrop callback function to handle selected files and preview the selected image.

Setting up drag-and-drop with react-dropzone

Jump to this spot in the video  0:56 Ensure your React application is set up, then install react-dropzone with npm install react-dropzone. Import the useDropzone hook by adding import { useDropzone } from "react-dropzone". Set up the onDrop callback to log selected files, and destructure properties and methods like getRootProps and acceptedFiles to manage drag-and-drop actions.
React
import { useCallback, useState } from "react"
import { useDropzone } from "react-dropzone"
import "./App.css"

export default function MyDropzone() {
  const [dataURL, setDataURL] = useState(null)
  const [uploadedURL, setUploadedURL] = useState(null)

  const onDrop = useCallback(acceptedFiles => {
    acceptedFiles.forEach(file => {
      const reader = new FileReader()
      reader.onabort = () => console.log("file reading was aborted")
      reader.onerror = () => console.log("file reading has failed")
      reader.onload = () => {
        const binaryStr = reader.result
        setDataURL(binaryStr)
      }
      reader.readAsDataURL(file)
    })
  }, [])

  const {
    getRootProps,
    acceptedFiles,
    getInputProps,
    isDragActive,
  } = useDropzone({ onDrop })

Spreading react-dropzone props in a <div>

Jump to this spot in the video  1:22 Spread the props from getRootProps() and getInputProps() onto the <div> to make it handle drag-and-drop events.
Html
<div className="drop-zone" {...getRootProps()}>
    <input {...getInputProps()} />

Accessing selected files

Jump to this spot in the video  1:29 Use acceptedFiles to access the array of selected files. Try logging the first selected file with console.log to see it in action.
React
  const {
    getRootProps,
    acceptedFiles,
    getInputProps,
    isDragActive,
  } = useDropzone({ onDrop })

  const selectedFile = acceptedFiles[0]
  console.log(selectedFile)

Setting state for data URL

Jump to this spot in the video  1:47 In the onDrop function, loop through the selected files (in this example, just one file). Use the FileReader API to read the file and listen for the onload event. Once the file has been read, set the file's dataURL as a state variable to display an image preview.
React
export default function MyDropzone() {
  const [dataURL, setDataURL] = useState(null)
  const [uploadedURL, setUploadedURL] = useState(null)

  const onDrop = useCallback(acceptedFiles => {
    acceptedFiles.forEach(file => {
      const reader = new FileReader()
      reader.onabort = () => console.log("file reading was aborted")
      reader.onerror = () => console.log("file reading has failed")
      reader.onload = () => {
        const binaryStr = reader.result
        setDataURL(binaryStr)
      }
      reader.readAsDataURL(file)
    })
  }, [])

Implementing visual feedback for drag-and-drop

Jump to this spot in the video  2:05 Implement visual feedback by checking isDragActive, which becomes true when a file is being dragged over the drop zone. Use this state to show a message or overlay, and if dataURL is not null, display a preview of the selected image along with options to upload or cancel. The buttons displayed on the preview provide a way to either cancel the action or upload the selected image, triggering the uploadImage function.
React
  return (
    <div className="container">
      <div className="zone">
        {dataURL ? (
          <div className="selected">
            <img src={dataURL} />
            <div className="actions">
              {uploadedURL ? (
                <span className="uploaded-txt">Uploaded!</span>
              ) : (
                <button
                  onClick={uploadImage}
                  className="upload-btn"
                >
                  Upload
                </button>
              )}
              <button
                onClick={() => setDataURL(null)}
                className="cancel-btn"
              >
                Cancel
              </button>
            </div>
          </div>
        ) 
        <div className="drop-zone" {...getRootProps()}>
            <input {...getInputProps()} />
            {isDragActive ? (
              <div className="drop-files">
                <svg
                  xmlns="http://www.w3.org/2000/svg"
                  viewBox="0 0 24 24"
                  height="50"
                  width="50"
                  fill="currentColor"
                >
                  <path d="M1 14.5C1 12.1716 2.22429 10.1291 4.06426 8.9812C4.56469 5.044 7.92686 2 12 2C16.0731 2 19.4353 5.044 19.9357 8.9812C21.7757 10.1291 23 12.1716 23 14.5C23 17.9216 20.3562 20.7257 17 20.9811L7 21C3.64378 20.7257 1 17.9216 1 14.5ZM16.8483 18.9868C19.1817 18.8093 21 16.8561 21 14.5C21 12.927 20.1884 11.4962 18.8771 10.6781L18.0714 10.1754L17.9517 9.23338C17.5735 6.25803 15.0288 4 12 4C8.97116 4 6.42647 6.25803 6.0483 9.23338L5.92856 10.1754L5.12288 10.6781C3.81156 11.4962 3 12.927 3 14.5C3 16.8561 4.81833 18.8093 7.1517 18.9868L7.325 19H16.675L16.8483 18.9868ZM13 13V17H11V13H8L12 8L16 13H13Z"></path>
                </svg>
              </div>
            ) : (
              <div className="drag-files">
                Drop your files here or click to browse
              </div>
            )}
          </div>
        )}
      </div>

Setting Up Cloudinary for file uploads

Jump to this spot in the video  3:07 To upload files to Cloudinary, you can use various methods. This tutorial shows how to use the upload endpoint to perform an unsigned upload for testing purposes, though signed uploads are recommended for production. Get your API key and save your credentials in a .env file. Then, create an unsigned upload preset.

Creating the uploadImage function

Jump to this spot in the video  3:58 Create an uploadImage function that uploads the file to Cloudinary using the provided API endpoint and upload preset. After uploading, store the returned URL and update the state with the response.
React
  const uploadImage = async () => {
    let formData = new FormData()

    formData.append("file", selectedFile)
    formData.append("upload_preset", import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET)
    formData.append("api_key", import.meta.env.VITE_CLOUDINARY_API_KEY)

    await fetch(`https://api.cloudinary.com/v1_1/${import.meta.env.VITE_CLOUDINARY_CLOUD_NAME}/image/upload`, {
      method: "POST",
      body: formData,
    })
      .then(r => {
        return r.json()
      })
      .then(data => {
        setUploadedURL(data.url)
      })

  }

Displaying the uploaded file

Jump to this spot in the video  4:32 After a successful upload, update the state with the public URL from the Cloudinary response and display the uploaded image in your application.
React
      {uploadedURL && (
        <a target="_blank" href={uploadedURL}>
          <span className="uploaded-url">{uploadedURL}</span>
        </a>
      )}

Testing drag-and-drop functionality

Jump to this spot in the video  4:37 Test your drag-and-drop functionality with Cloudinary integration. Drag a file into the drop zone, preview it, and then upload it to Cloudinary. Once uploaded, the file’s public URL is displayed, confirming successful integration.

Keep learning

Related topics

If you like this, you might also like...

 

Cloudinary Academy

 

Check out the Cloudinary Academy for free self-paced Cloudinary courses on a variety of developer or DAM topics, or register for formal instructor-led courses, either virtual or on-site.

 

✔️ Feedback sent!