# react-dropzone Simple HTML5 drag 'n' drop zone with React.js # Getting Started `react-dropzone` is a set of React hooks and components for creating a drag 'n' drop zone for files. ## Installation :::code-group ```bash [npm] npm install react-dropzone ``` ```bash [pnpm] pnpm add react-dropzone ``` ```bash [yarn] yarn add react-dropzone ``` ::: `react-dropzone` ships as ESM and CommonJS with TypeScript types included, and lists `react` as a peer dependency (`>= 18`). :::warning[Breaking change] `FileWithPath` (re-exported from [file-selector](https://github.com/react-dropzone/file-selector)) now types `path` and `relativePath` as **required** — a plain `File` is no longer assignable to it. The `onDrop`/`onDropAccepted` callbacks still hand you `File` objects, so type those handlers as `File[]` rather than `FileWithPath[]`. ::: ## Usage Use the `useDropzone` hook to bind the necessary handlers to any element: ```tsx import React from "react"; import {useDropzone} from "react-dropzone"; function MyDropzone() { const {getRootProps, getInputProps} = useDropzone({ onDrop: acceptedFiles => { // Do something with the files, e.g. upload to a server console.log(acceptedFiles); } }); return (

Drag 'n' drop some files here, or click to select files

); } ``` * `getRootProps()` returns the props for the root drag 'n' drop container. * `getInputProps()` returns the props for the hidden `` used for click/keyboard access. See the [Basic example](/examples/basic) for a live, interactive version. # Using with Tauri (desktop apps) Inside a [Tauri](https://tauri.app/) webview, dragging a file onto the window does **not** fire the browser's `dragenter`/`dragover`/`drop` events by default, so a `react-dropzone` dropzone appears to ignore drops. This is not a bug in `react-dropzone`: Tauri's native window layer intercepts OS file drops **before** the webview sees them and emits its own events instead. This has been the behaviour since the early betas ([tauri-apps/tauri#2768](https://github.com/tauri-apps/tauri/issues/2768)). You have two options, depending on whether you need the real filesystem paths of dropped files. ## Recommended: let the webview handle drops Disable Tauri's native drag-and-drop handling. Once it's off, standard HTML5 drag-and-drop events flow to the webview and `react-dropzone` works with **no code changes** - drag state, `onDrop`, validation, everything. Disabling it is also required to use HTML5 drag-and-drop on Windows. :::code-group ```json [Tauri v2 - tauri.conf.json] { "app": { "windows": [ { "dragDropEnabled": false } ] } } ``` ```json [Tauri v1 - tauri.conf.json] { "tauri": { "windows": [ { "fileDropEnabled": false } ] } } ``` ::: :::warning[Trade-off] With native handling off you get sandboxed browser `File` objects, exactly as in a normal browser - you do **not** get the absolute filesystem path of a dropped file. If you need real paths, keep native handling on and use the approach below. ::: ## If you need absolute file paths: keep native handling on Keep `dragDropEnabled` at its default (`true`) and listen to Tauri's own drag-and-drop event, which hands you the absolute paths. Read each path into a `File` with the [fs plugin](https://v2.tauri.app/plugin/file-system/), then run it through your own validation or `react-dropzone`'s `getFilesFromEvent` helper. In this mode `react-dropzone` is only driving the click-to-open picker and your styling - the drop itself and the drag-active state are yours to manage, since the DOM drag events never fire. ```tsx import {getCurrentWebviewWindow} from "@tauri-apps/api/webviewWindow"; import {readFile} from "@tauri-apps/plugin-fs"; const webview = getCurrentWebviewWindow(); const unlisten = await webview.onDragDropEvent(async event => { // event.payload.type is "enter" | "over" | "drop" | "leave" if (event.payload.type === "drop") { const files = await Promise.all( event.payload.paths.map(async path => { const bytes = await readFile(path); const name = path.split(/[\\/]/).pop() ?? path; return new File([bytes], name); }) ); // Hand `files` to your own state, or validate them with react-dropzone's // getFilesFromEvent / the same accept/size rules you pass to useDropzone. } }); ``` :::note `react-dropzone`'s built-in drop pipeline can't currently be driven directly from Tauri's events - that would need a pluggable event source in the core, tracked in [#1316](https://github.com/react-dropzone/react-dropzone/issues/1316). For now, manage the dropped files in your own React state and derive the drag-active UI from the `enter`/`over`/ `leave` payload types. ::: ## Version notes Tauri renamed the config flag and the events between v1 and v2: | | Tauri v1 | Tauri v2 | | ----------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | Config flag | `tauri.windows[].fileDropEnabled` | `app.windows[].dragDropEnabled` | | JS listener | `appWindow.onFileDropEvent()` | `getCurrentWebviewWindow().onDragDropEvent()` | | Events | `tauri://file-drop`, `tauri://file-drop-hover`, `tauri://file-drop-cancelled` | `tauri://drag-enter`, `tauri://drag-over`, `tauri://drag-drop`, `tauri://drag-leave` | Both default to enabled. Setting the flag to `false` is what lets `react-dropzone` handle drops itself. ## References * [Tauri config reference - `dragDropEnabled`](https://v2.tauri.app/reference/config/) * [Tauri drag-and-drop discussion (v1/v2)](https://github.com/tauri-apps/tauri/discussions/9696) * [tauri-apps/tauri#2768 - original "no support for dropping files?"](https://github.com/tauri-apps/tauri/issues/2768) # Basic example The `useDropzone` hook just binds the necessary handlers to create a drag 'n' drop zone. Use the `getRootProps()` fn to get the props required for drag 'n' drop and use them on any element. For click and keydown behavior, use the `getInputProps()` fn and use the returned props on an ``. Furthermore, the hook supports folder drag 'n' drop by default. See [file-selector](https://github.com/react-dropzone/file-selector) for more info about supported browsers. ```tsx import React from "react"; import {useDropzone} from "react-dropzone"; function Basic() { const {acceptedFiles, getRootProps, getInputProps} = useDropzone(); const files = acceptedFiles.map(file => (
  • {file.path} - {file.size} bytes
  • )); return (

    Drag 'n' drop some files here, or click to select files

    ); } ``` ## Disabled Dropzone with the `disabled` property: ```tsx const {getRootProps, getInputProps} = useDropzone({disabled: true}); ``` # Event propagation If you'd like to prevent drag events propagation from the child to parent, use the `noDragEventsBubbling` property on the child. Note how the outer `onDrop` is never invoked when the drop occurs on the inner dropzone: ```tsx function InnerDropzone() { const {getRootProps} = useDropzone({noDragEventsBubbling: true}); return (

    Inner dropzone

    ); } function OuterDropzone() { const {getRootProps} = useDropzone({onDrop: files => console.log(files)}); return (

    Outer dropzone

    ); } ``` ## Disabling click Turn off the default click behavior with `noClick`: ```tsx const {getRootProps, getInputProps} = useDropzone({noClick: true}); ``` ## Disabling keyboard Turn off SPACE/ENTER and focus handling with `noKeyboard`: ```tsx const {getRootProps, getInputProps} = useDropzone({noKeyboard: true}); ``` ## Disabling drag Turn off drag 'n' drop with `noDrag`: ```tsx const {getRootProps, getInputProps} = useDropzone({noDrag: true}); ``` ## Stopping propagation yourself If you provide your own handlers and call `event.stopPropagation()`, it prevents the default dropzone behavior (nothing is logged on drop here): ```tsx console.log(files)}> {({getRootProps, getInputProps}) => (
    event.stopPropagation()})}>
    )}
    ``` # Using Dropzone inside a form react-dropzone does not submit files in form submissions by default. If you need this, add a hidden file input and set the files into it. :::warning[Spreading `getRootProps()` onto a ` ); } ``` # Styling the dropzone The hook doesn't set any styles on either of the prop fns (`getRootProps()` / `getInputProps()`) — you're in full control. ## Using inline styles ```tsx const baseStyle = {/* ... */}; const focusedStyle = {borderColor: "#2196f3"}; const acceptStyle = {borderColor: "#00e676"}; const rejectStyle = {borderColor: "#ff1744"}; function StyledDropzone() { const {getRootProps, getInputProps, isFocused, isDragAccept, isDragReject} = useDropzone({ accept: {"image/*": []} }); const style = useMemo( () => ({ ...baseStyle, ...(isFocused ? focusedStyle : {}), ...(isDragAccept ? acceptStyle : {}), ...(isDragReject ? rejectStyle : {}) }), [isFocused, isDragAccept, isDragReject] ); return (

    Drag 'n' drop some files here, or click to select files

    ); } ``` ## Using styled-components ```tsx import styled from "styled-components"; const getColor = props => { if (props.isDragAccept) return "#00e676"; if (props.isDragReject) return "#ff1744"; if (props.isFocused) return "#2196f3"; return "#eeeeee"; }; const Container = styled.div` /* ... */ border-color: ${props => getColor(props)}; `; function StyledDropzone() { const {getRootProps, getInputProps, isFocused, isDragAccept, isDragReject} = useDropzone({ accept: {"image/*": []} }); return (

    Drag 'n' drop some files here, or click to select files

    ); } ``` # Drag overlay (`isDragGlobal`) The `isDragGlobal` state is `true` when files are being dragged anywhere on the document, before they reach the dropzone. This lets you show visual feedback (like a full-page overlay) to indicate where files can be dropped. ```tsx function DragOverlay() { const {getRootProps, getInputProps, isDragGlobal, isDragActive, isDragAccept, isDragReject} = useDropzone({ accept: {"image/*": [".png", ".jpg", ".jpeg", ".gif"]} }); return (
    {isDragGlobal && !isDragActive &&
    Drop files anywhere on this page...
    }
    {isDragGlobal && !isDragActive &&

    🌐 Drag detected on page!

    } {isDragAccept &&

    ✅ Drop to upload these files

    } {isDragReject &&

    ❌ Some files will be rejected

    }
    ); } ``` `isDragGlobal` resets to `false` when the drag leaves the document, files are dropped anywhere, or the drag is cancelled (ESC). # Accepting specific file types By providing the `accept` prop you can make the dropzone accept specific file types and reject the others. The value is an object keyed by [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) with an array of file extensions as values. Pairing a wildcard MIME type with extensions narrows it down to those extensions - `{"image/*": [".jpeg", ".png"]}` accepts only `.jpeg` and `.png` files, not every image type. Use an empty array (`{"image/*": []}`) to accept all files of that type. ```tsx function Accept() { const {acceptedFiles, fileRejections, getRootProps, getInputProps} = useDropzone({ accept: {"image/jpeg": [], "image/png": []} }); const acceptedFileItems = acceptedFiles.map(file => (
  • {file.path} - {file.size} bytes
  • )); const fileRejectionItems = fileRejections.map(({file, errors}) => (
  • {file.path} - {file.size} bytes
      {errors.map(e => (
    • {e.message}
    • ))}
  • )); return (

    Drag 'n' drop some files here, or click to select files

    (Only *.jpeg and *.png images will be accepted)
    ); } ``` ## Grouping types for the file picker When the [File System Access picker](/examples/file-dialog) is used (`useFsAccessApi: true`, in a secure context, on a supporting browser), `showOpenFilePicker` renders one filter row per *group* of types. To control that grouping and label each row, pass `accept` as an **array** of `{description, accept}` entries instead of a single map: ```tsx const {getRootProps, getInputProps} = useDropzone({ useFsAccessApi: true, accept: [ {description: "Images", accept: {"image/jpeg": [".jpg", ".jpeg"], "image/png": []}}, {description: "Documents", accept: {"application/pdf": [".pdf"]}} ] }); ``` The picker then shows two rows - "Images" and "Documents" - rather than a single combined entry. `description` is optional; when omitted it is derived from the group's extensions (the plain object form, which can't express a description, is labeled this way too). Extension values may be a single string or an array (`".pdf"` and `[".pdf"]` are equivalent), matching `showOpenFilePicker`. > Grouping only affects the File System Access picker. On the native `` fallback - the default, and where the browser doesn't support the FS Access API - all groups are flattened into one `accept` attribute and the descriptions are dropped. Accepted/rejected file validation is identical to listing the same types in the flat object form. ## Reacting during the drag Because of HTML5 File API limitations, file names/extensions aren't readable *during* a drag, so use MIME types (e.g. `image/*`) if you want to react with `isDragAccept` / `isDragReject`: ```tsx const {isDragActive, isDragAccept, isDragReject, getRootProps, getInputProps} = useDropzone({ accept: {"image/*": [".jpeg", ".png"]} }); ``` > Mime type determination is not reliable across platforms. CSV files, for example, are reported as `text/plain` under macOS but as `application/vnd.ms-excel` under Windows. ### `isDragUnknown` and custom validators Your custom `validator` is typed `(file: File) => …` and usually reads `file.name`, `file.size`, etc. During a drag those aren't available (the browser only exposes a MIME `type`), so react-dropzone **doesn't run the validator until drop**. While a validator is configured and the built-in checks pass, the drag state is neither accept nor reject but `isDragUnknown` — the outcome can only be confirmed once the real files are dropped: ```tsx const {isDragAccept, isDragReject, isDragUnknown, getRootProps, getInputProps} = useDropzone({ validator: myValidator }); // isDragUnknown === true → "these files might be rejected on drop" ``` A file whose MIME type is *confidently* wrong (or a selection that breaks `multiple`/`maxFiles`) is still `isDragReject` during the drag, even with a validator — a validator can only ever add rejections on drop, never rescue one. ## Restoring the full MIME-type table `react-dropzone` resolves each file's MIME `type` through [file-selector](https://github.com/react-dropzone/file-selector)'s `fromEvent` — the default for the [`getFilesFromEvent`](/examples/plugins) prop — which infers the `type` from the file extension when the browser leaves a file typeless (common with drag 'n' drop and File System Access sources). As of `file-selector` v4, only a **small built-in set** of common extensions is bundled, so the core stays lightweight. Because `accept` matches by file **extension** as well as MIME type, most configurations keep working regardless. You only need the full table when matching purely by MIME type (e.g. `image/*`) against typeless sources — pass the full `COMMON_MIME_TYPES` table via `getFilesFromEvent` (add `file-selector` to your dependencies to import from it): ```bash npm install file-selector ``` ```tsx import {useDropzone} from "react-dropzone"; import {fromEvent} from "file-selector"; import {COMMON_MIME_TYPES} from "file-selector/mime"; function FullMimeCoverage() { const {getRootProps, getInputProps} = useDropzone({ getFilesFromEvent: event => fromEvent(event, {mimeTypes: COMMON_MIME_TYPES}) }); // ... } ``` # Accepting a specific number of files By providing the `maxFiles` prop you can limit how many files the dropzone accepts. It applies when `multiple` is enabled; the default of `0` means no limit. When more valid files are dropped than allowed, the first `maxFiles` are accepted and each surplus file is rejected with a `too-many-files` error (rather than rejecting the whole batch). When `multiple` is `false` the limit is always `1`, so the first valid file is accepted and the rest are rejected the same way. Files that fail their own checks (wrong type, out of the `[minSize, maxSize]` range, or a custom `validator` error) are rejected with those errors and don't count towards the limit. ```tsx function AcceptMaxFiles() { const {acceptedFiles, fileRejections, getRootProps, getInputProps} = useDropzone({maxFiles: 2}); const acceptedFileItems = acceptedFiles.map(file => (
  • {file.path} - {file.size} bytes
  • )); const fileRejectionItems = fileRejections.map(({file, errors}) => (
  • {file.path} - {file.size} bytes
      {errors.map(e => (
    • {e.message}
    • ))}
  • )); return (

    Drag 'n' drop some files here, or click to select files

    (2 files are the maximum number of files you can drop here)
    ); } ``` # Custom validation By providing the `validator` prop you can specify custom validation for files. It must return `null` if the file should be accepted, or an error object / array of error objects if it should be rejected. ```tsx const maxLength = 20; function nameLengthValidator(file) { if (file.name.length > maxLength) { return { code: "name-too-large", message: `Name is larger than ${maxLength} characters` }; } return null; } function CustomValidation() { const {acceptedFiles, fileRejections, getRootProps, getInputProps} = useDropzone({ validator: nameLengthValidator }); // ...render accepted/rejected lists } ``` ## Async validation The `validator` may be `async` (return a `Promise`) for checks that can't run synchronously - reading image dimensions or video duration, inspecting file contents with a library like [`file-type`](https://www.npmjs.com/package/file-type), or calling an external service. Resolve with `null` to accept the file, or an error object / array to reject it. While a drop is being processed asynchronously, `isProcessing` is `true` and `onDrop`/`onDropAccepted`/`onDropRejected` fire only once it settles - use it to show a spinner or disable the UI. It spans the whole pipeline: reading the files (an async [`getFilesFromEvent`](/examples/plugins)) and running an async validator. With the default synchronous file reading and no async validator, the work resolves within a microtask, so `isProcessing` is only observable for genuinely async work. If the validator throws or rejects, the drop is discarded and `onError` is called with the error. If a new drop lands while one is still processing, the earlier run is superseded and only the latest result is committed. ```tsx async function imageDimensionsValidator(file) { const bitmap = await createImageBitmap(file); if (bitmap.width < 250) { return {code: "too-narrow", message: "Image must be at least 250px wide"}; } return null; } function AsyncValidation() { const {getRootProps, getInputProps, isProcessing} = useDropzone({ validator: imageDimensionsValidator, accept: {"image/*": []}, onError: err => console.error(err) }); return (

    {isProcessing ? "Validating…" : "Drag 'n' drop some images here"}

    ); } ``` > The validator never runs during a drag (a dragged item exposes no name/size), so a validator-configured > dropzone reports `isDragUnknown` until drop - this is unchanged for async validators. ## Overriding error messages The built-in rejection messages (`file-invalid-type`, `file-too-large`, `file-too-small`, `too-many-files`) are in English. Use the `getErrorMessage` prop to override them - e.g. to localize. It's called once per error with the error and the file it belongs to, and its return value replaces the message. Return `error.message` for codes you don't want to change (this also applies to your own `validator` errors). ```tsx function LocalizedDropzone() { const {getRootProps, getInputProps} = useDropzone({ maxSize: 1024, getErrorMessage: (error, file) => { switch (error.code) { case "file-too-large": return `${file.name} dépasse la taille maximale`; case "too-many-files": return "Trop de fichiers"; default: return error.message; } } }); // ... } ``` # Opening the file dialog programmatically You can open the native file prompt with the `open` method returned by the hook. Most browsers require the call to originate from a direct user interaction (e.g. a click). Note the `type="button"` on the button below: inside a `` a ` ); } ``` ## Using the component ref Or use the `ref` exposed by the `` component: ```tsx import Dropzone, {DropzoneRef} from "react-dropzone"; function RefDropzone() { const dropzoneRef = useRef(null); return ( {({getRootProps, getInputProps}) => (
    )}
    ); } ``` # Selecting a directory Passing the non-standard [`webkitdirectory`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#webkitdirectory) attribute through `getInputProps` makes clicking the dropzone open a **folder** picker instead of a file picker. The browser recursively lists the folder's files, each with its relative path exposed on `file.path` (via [file-selector](https://github.com/react-dropzone/file-selector)). ```tsx function Directory() { const {acceptedFiles, getRootProps, getInputProps} = useDropzone(); const files = acceptedFiles.map(file => (
  • {file.path} - {file.size} bytes
  • )); return (

    Drag 'n' drop a folder here, or click to select a folder

    ); } ``` > Directory selection works only through the native ``, so it requires `useFsAccessApi` to be `false` (the default) - the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) cannot select directories. Note that `webkitdirectory`, while widely supported, is [non-standard](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#webkitdirectory). ## TypeScript React's built-in types don't declare `webkitdirectory`, so TypeScript rejects it on ``. Since it's a global attribute, declare it once in your own project (e.g. a `react-app-env.d.ts`) rather than reaching for a cast: ```ts import "react"; declare module "react" { interface InputHTMLAttributes { webkitdirectory?: string; } } ``` react-dropzone deliberately doesn't ship this augmentation itself - a library-level `declare module "react"` would apply to your whole app and could clash with another declaration of the same attribute. # Previews Since version 7.0.0, `react-dropzone` no longer generates a `preview` on `File` objects. You can create previews yourself in the `onDrop()` callback: ```tsx function Previews() { const [files, setFiles] = useState([]); const {getRootProps, getInputProps} = useDropzone({ accept: {"image/*": []}, onDrop: acceptedFiles => { setFiles( acceptedFiles.map(file => Object.assign(file, { preview: URL.createObjectURL(file) }) ) ); } }); const thumbs = files.map(file => (
    URL.revokeObjectURL(file.preview)} alt={file.name} />
    )); useEffect(() => { // Revoke the data uris to avoid memory leaks on unmount return () => files.forEach(file => URL.revokeObjectURL(file.preview)); }, [files]); return (

    Drag 'n' drop some files here, or click to select files

    ); } ``` # Class components If you're still using class components, use the `` component provided by the lib: ```tsx import React, {Component} from "react"; import Dropzone from "react-dropzone"; class Basic extends Component { constructor() { super(); this.onDrop = files => this.setState({files}); this.state = {files: []}; } render() { const files = this.state.files.map(file => (
  • {file.name} - {file.size} bytes
  • )); return ( {({getRootProps, getInputProps}) => (

    Drag 'n' drop some files here, or click to select files

    )}
    ); } } ``` # No JSX If you'd like to use [React without JSX](https://react.dev/reference/react/createElement), you can: ```jsx import React, {useCallback, useState} from "react"; import {useDropzone} from "react-dropzone"; const e = React.createElement; function Basic() { const [files, setFiles] = useState([]); const onDrop = useCallback(files => setFiles(files), [setFiles]); const {getRootProps, getInputProps} = useDropzone({onDrop}); const fileList = files.map(file => e("li", {key: file.name}, `${file.name} - ${file.size} bytes`)); return e("section", {className: "container"}, [ e("div", getRootProps({className: "dropzone", key: "dropzone"}), [ e("input", getInputProps({key: "input"})), e("p", {key: "desc"}, "Drag 'n' drop some files here, or click to select files") ]), e("aside", {key: "filesContainer"}, [e("h4", {key: "title"}, "Files"), e("ul", {key: "fileList"}, fileList)]) ]); } ``` # Extending the dropzone (plugins) The hook accepts a `getFilesFromEvent` prop that lets you customize how dropped file-system objects are handled — e.g. resolving a dropped folder to an array of files. The provided function must return a `Promise` with a list of `File` objects (or `DataTransferItem` of `{kind: 'file'}`). To add properties to a `File`, use [Object.defineProperty()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) so the result still passes through the internal filter: ```tsx async function myCustomFileGetter(event) { const files = []; const fileList = event.dataTransfer ? event.dataTransfer.files : event.target.files; for (let i = 0; i < fileList.length; i++) { const file = fileList.item(i); Object.defineProperty(file, "myProp", {value: true}); files.push(file); } return files; } function Plugin() { const {acceptedFiles, getRootProps, getInputProps} = useDropzone({ getFilesFromEvent: event => myCustomFileGetter(event) }); // ...render acceptedFiles, each with f.myProp } ``` # Pintura integration If you'd like to integrate the dropzone with the [Pintura](https://pqina.nl/pintura/?ref=react-dropzone) image editor, pass a selected image to the `openDefaultEditor()` method exported by Pintura. :::info This example uses the commercial `pintura` package, so it's shown as static code rather than a live demo. ::: ```tsx import React, {useState, useEffect} from "react"; import {useDropzone} from "react-dropzone"; import "pintura/pintura.css"; import {openDefaultEditor} from "pintura"; // Called when the user taps the edit button: opens the editor and returns the modified file. const editImage = (image, done) => { const imageFile = image.pintura ? image.pintura.file : image; const imageState = image.pintura ? image.pintura.data : {}; const editor = openDefaultEditor({src: imageFile, imageState}); editor.on("process", ({dest, imageState}) => { Object.assign(dest, {pintura: {file: imageFile, data: imageState}}); done(dest); }); }; function App() { const [files, setFiles] = useState([]); const {getRootProps, getInputProps} = useDropzone({ accept: {"image/*": []}, onDrop: acceptedFiles => { setFiles(acceptedFiles.map(file => Object.assign(file, {preview: URL.createObjectURL(file)}))); } }); const thumbs = files.map((file, index) => (
    )); useEffect( () => () => { files.forEach(file => URL.revokeObjectURL(file.preview)); }, [files] ); return (

    Drag 'n' drop some files here, or click to select files

    ); } export default App; ```