Last updated: February 5, 2025

<span aria-hidden="true" id="implementing-drag-and-drop-file-upload-in-react"></span>

# Implementing drag and drop file upload in React

![Tim Koschützki](/assets/images/teammates/avatar-tim-kos-1.jpg?dpl=dpl_C6YH6XrtnwbHLJcm1LDKywQn4CB3)

#### Tim Koschützki

Co-founder · Berlin, Germany · Show bio

[](https://x.com/tim%5Fkos)[](https://github.com/tim-kos)

Drag and drop file upload functionality enhances user experience by providing an intuitive way to handle files in web applications. In this post, we use the well-supported library react-dropzone to build a robust, type-safe file upload component in React.

<span aria-hidden="true" id="setting-up-the-project"></span>

## Setting up the project

Begin by installing the required dependency using either npm or yarn:

```bash
npm install react-dropzone
# Or
yarn add react-dropzone

```

<span aria-hidden="true" id="creating-a-typescript-file-upload-component"></span>

## Creating a TypeScript file upload component

Below is a complete example of a drag and drop file upload component implemented with React and TypeScript. This component leverages react-dropzone to manage file selection, validation, and error handling.

```tsx
import React, { useCallback, useState } from 'react'
import { useDropzone, FileRejection } from 'react-dropzone'

interface UploadProps {
  onUploadComplete?: (files: File[]) => void
  maxFiles?: number
  maxSize?: number
}

const DragAndDropUpload: React.FC<UploadProps> = ({
  onUploadComplete,
  maxFiles = 1,
  maxSize = 5242880, // 5MB
}) => {
  const [files, setFiles] = useState<File[]>([])
  const [uploadError, setUploadError] = useState<string | null>(null)

  const onDropAccepted = useCallback((acceptedFiles: File[]) => {
    setFiles(acceptedFiles)
    setUploadError(null)
    if (onUploadComplete) {
      onUploadComplete(acceptedFiles)
    }
  }, [onUploadComplete])

  const onDropRejected = useCallback((fileRejections: FileRejection[]) => {
    const errors = fileRejections.map(
      (rejection) => `${rejection.file.name}: ${rejection.errors[0].message}`
    )
    setUploadError(errors.join('\n'))
  }, [])

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDropAccepted,
    onDropRejected,
    maxFiles,
    maxSize,
    accept: {
      'image/*': ['.jpeg', '.jpg', '.png', '.gif'],
      'application/pdf': ['.pdf'],
    },
  })

  return (
    <div className="upload-container">
      <div
        {...getRootProps()}
        className={`dropzone ${isDragActive ? 'active' : ''}`}
      >
        <input {...getInputProps()} />
        {isDragActive ? (
          <p>Drop the files here</p>
        ) : (
          <p>Drag and drop files here, or click to select files</p>
        )}
      </div>

      {uploadError && <div className="error-message">{uploadError}</div>}

      {files.length > 0 && (
        <div className="file-list">
          <h4>Selected Files:</h4>
          <ul>
            {files.map((file) => (
              <li key={file.name}>
                {file.name} - {(file.size / 1024).toFixed(2)} KB
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  )
}

export default DragAndDropUpload

```

<span aria-hidden="true" id="handling-file-uploads"></span>

## Handling file uploads

This function demonstrates how to handle file uploads using the Fetch API along with proper error handling. It converts the selected files into a FormData object and sends them to your back-end API endpoint.

```typescript
const uploadFiles = async (files: File[]): Promise<void> => {
  const formData = new FormData()
  files.forEach((file) => {
    formData.append('files', file)
  })

  try {
    const response = await fetch('/api/upload', {
      method: 'POST',
      body: formData,
      headers: {
        Accept: 'application/json',
      },
    })

    if (!response.ok) {
      const errorData = await response.json()
      throw new Error(errorData.message || 'Upload failed')
    }

    const result = await response.json()
    console.log('Upload successful:', result)
  } catch (error: any) {
    console.error('Upload error:', error)
    throw error
  }
}

```

<span aria-hidden="true" id="styling-the-component"></span>

## Styling the component

The following CSS styles help create a clean and responsive design for the upload component. Adjust these styles as needed for your application.

```css
.upload-container {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.dropzone {
  border: 2px dashed #0087f7;
  border-radius: 4px;
  padding: 20px;
  text-align: center;
  background: #f8f9fa;
  cursor: pointer;
  transition: all 0.3s ease;
}

.dropzone.active {
  border-color: #00e676;
  background: #e3f2fd;
}

.error-message {
  color: #d32f2f;
  margin-top: 10px;
  padding: 10px;
  background: #ffebee;
  border-radius: 4px;
}

.file-list {
  margin-top: 20px;
}

.file-list ul {
  list-style: none;
  padding: 0;
}

.file-list li {
  padding: 8px;
  background: #f5f5f5;
  margin-bottom: 8px;
  border-radius: 4px;
}

```

<span aria-hidden="true" id="advanced-file-uploads-with-uppy"></span>

## Advanced file uploads with Uppy

For projects that require advanced features such as upload progress tracking, chunked uploads, or resumable uploads, Uppy is an excellent alternative. The snippet below demonstrates how to integrate Uppy (version 4.x) with React.

```jsx
import Uppy from '@uppy/core'
import { Dashboard } from '@uppy/react'
import '@uppy/core/dist/style.min.css'
import '@uppy/dashboard/dist/style.min.css'

const uppy = new Uppy()

function UppyUploader() {
  return <Dashboard uppy={uppy} />
}

export default UppyUploader

```

Uppy builds on modern web standards and works seamlessly with React, making it a powerful choice for handling complex file uploads.

<span aria-hidden="true" id="accessibility-and-mobile-considerations"></span>

## Accessibility and mobile considerations

When implementing drag and drop file uploads, consider the following to ensure a great user experience across all devices:

* Ensure the dropzone is keyboard accessible by adding focus styles or attributes if needed.
* Use clear and concise instructions so users understand how to interact with the component.
* Test the dropzone on mobile devices to verify that fallback behaviors (such as file selection via taps) work reliably.
* Consider adding ARIA attributes, such as `aria-label`, to improve accessibility for screen readers.

<span aria-hidden="true" id="usage-example"></span>

## Usage example

Integrate the DragAndDropUpload component into your application as shown below. In this example, the parent component handles file uploads by invoking the `uploadFiles` function upon completion of the file selection.

```tsx
import React from 'react'
import DragAndDropUpload from './DragAndDropUpload'

const App: React.FC = () => {
  const handleUploadComplete = async (files: File[]) => {
    try {
      await uploadFiles(files)
      // Additional success handling can go here
    } catch (error) {
      console.error('Upload failed:', error)
    }
  }

  return (
    <div>
      <h1>File Upload</h1>
      <DragAndDropUpload
        onUploadComplete={handleUploadComplete}
        maxFiles={5}
        maxSize={5242880}
      />
    </div>
  )
}

export default App

```

<span aria-hidden="true" id="conclusion"></span>

## Conclusion

In this post, we built a robust, type-safe drag and drop file upload component using react-dropzone. We covered setting up the project, implementing file validation and error handling, and styling the component. Additionally, we explored how Uppy can be integrated for advanced upload features and offered tips for ensuring accessibility and mobile friendliness.

For more advanced features like upload progress tracking, chunked uploads, and resumable uploads, consider using Uppy—a powerful, open source file uploader developed by Transloadit ([https://uppy.io⁠](https://uppy.io)).

\#react#file-upload#drag-and-drop#handling-uploads-service#upload-handle-robot

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed · 5 GB included in the free plan

Cancel anytime
