Dropzone

Capture files from user with drag and drop
Import
License

Installation

Package depends on @mantine/core and @mantine/hooks.

Install with yarn:

yarn add @mantine/dropzone

Install with npm:

npm install @mantine/dropzone

Usage

Dropzone lets you capture one or more files from user. Component is based on react-dropzone and support all of its core features:

  • Accepts/rejects files based on provided mime types
  • Limits individual file size
  • Renders given children and provides context based component to display elements based on current status
import { Group, Text, useMantineTheme, rem } from '@mantine/core';
import { IconUpload, IconPhoto, IconX } from '@tabler/icons-react';
import { Dropzone, DropzoneProps, IMAGE_MIME_TYPE } from '@mantine/dropzone';
export function BaseDemo(props: Partial<DropzoneProps>) {
const theme = useMantineTheme();
return (
<Dropzone
onDrop={(files) => console.log('accepted files', files)}
onReject={(files) => console.log('rejected files', files)}
maxSize={3 * 1024 ** 2}
accept={IMAGE_MIME_TYPE}
{...props}
>
<Group position="center" spacing="xl" style={{ minHeight: rem(220), pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload
size="3.2rem"
stroke={1.5}
color={theme.colors[theme.primaryColor][theme.colorScheme === 'dark' ? 4 : 6]}
/>
</Dropzone.Accept>
<Dropzone.Reject>
<IconX
size="3.2rem"
stroke={1.5}
color={theme.colors.red[theme.colorScheme === 'dark' ? 4 : 6]}
/>
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size="3.2rem" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" color="dimmed" inline mt={7}>
Attach as many files as you like, each file should not exceed 5mb
</Text>
</div>
</Group>
</Dropzone>
);
}

Dropzone.Accept, Dropzone.Reject and Dropzone.Idle

Dropzone.Accept, Dropzone.Reject and Dropzone.Idle components are visible only when the user performs certain action:

  • Dropzone.Accept is visible only when the user drags file that can be accepted over the dropzone
  • Dropzone.Reject is visible only when the user drags file that cannot be accepted over the dropzone
  • Dropzone.Idle is visible when the user does not drag anything over dropzone

Loading state

Set loading prop to indicate loading state with LoadingOverlay component. When loading props is true user cannot drop or select new files (Dropzone becomes disabled):

import { Dropzone } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone loading>
{/* children */}
</Dropzone>
);
}

Disabled state

If you want to implement your own loading state you can disable Dropzone without LoadingOverlay. Same as with loading, when Dropzone is disabled user cannot drop or select new files:

import { createStyles } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
// Add your own disabled styles
const useStyles = createStyles((theme) => ({
disabled: {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
borderColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[2],
cursor: 'not-allowed',
'& *': {
color: theme.colorScheme === 'dark' ? theme.colors.dark[3] : theme.colors.gray[5],
},
},
}));
function Demo() {
const { classes } = useStyles();
return (
<Dropzone disabled className={classes.disabled}>
{/* children, see previous demo */}
</Dropzone>
);
}

Open file browser manually

To open files browser from outside of component use openRef prop to get function that will trigger file browser:

import { useRef } from 'react';
import { Button, Group } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
function Demo() {
const openRef = useRef<() => void>(null);
return (
<>
<Dropzone openRef={openRef}>
{/* children */}
</Dropzone>
<Group position="center" mt="md">
<Button onClick={() => openRef.current()}>Select files</Button>
</Group>
</>
);
}

Enable child pointer event

By default, Dropzone disables pointer events on its children for dragging events to work. So when we set activateOnClick to false, clicking on any children inside Dropzone will do nothing. However, You can set style pointerEvents: 'all' to make children events to work:

import { useRef } from 'react';
import { Button, Group } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
function Demo() {
const openRef = useRef<() => void>(null);
return (
<>
<Dropzone
openRef={openRef}
onDrop={() => {}}
activateOnClick={false}
styles={{ inner: { pointerEvents: 'all' } }}
>
<Group position="center">
<Button onClick={() => openRef.current()}>Select files</Button>
</Group>
</Dropzone>
</>
);
}

Mime types

To specify file types provide an object with the keys set to the mime type and the values as an array of file extensions. Find more examples of accepting specific file types in the react-dropzone documentation.

import { Dropzone } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone
accept={{
'image/*': [], // All images
'text/html': ['.html', '.htm'],
}}
>
{/* children */}
</Dropzone>
);
}

You can also specify file types by providing an array of mime types to accept prop:

import { Dropzone } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone accept={['image/png', 'image/jpeg', 'image/sgv+xml', 'image/gif']}>
{/* children */}
</Dropzone>
);
}

To save some research time you can use MIME_TYPES variable exported from @mantine/dropzone:

import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone accept={[MIME_TYPES.png, MIME_TYPES.jpeg, MIME_TYPES.svg, MIME_TYPES.gif]}>
{/* children */}
</Dropzone>
);
}

MIME_TYPES includes following data:

KeyMime type
pngimage/png
gifimage/gif
jpegimage/jpeg
svgimage/svg+xml
webpimage/webp
avifimage/avif
mp4video/mp4
zipapplication/zip
csvtext/csv
pdfapplication/pdf
docapplication/msword
docxapplication/vnd.openxmlformats-officedocument.wordprocessingml.document
xlsapplication/vnd.ms-excel
xlsxapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet
pptapplication/vnd.ms-powerpoint
pptxapplication/vnd.openxmlformats-officedocument.presentationml.presentation
exeapplication/vnd.microsoft.portable-executable

Additionally you can use grouped mime types:

VariableMime types
IMAGE_MIME_TYPEimage/png, image/gif, image/jpeg, image/svg+xml, image/webp, image/avif
PDF_MIME_TYPEapplication/pdf
MS_WORD_MIME_TYPEapplication/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document
MS_EXCEL_MIME_TYPEapplication/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
MS_POWERPOINT_MIME_TYPEapplication/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation
import { IMAGE_MIME_TYPE, Dropzone } from '@mantine/dropzone';
function Demo() {
return <Dropzone accept={IMAGE_MIME_TYPE}>{/* children */}</Dropzone>;
}

Styles API

Dropzone root element has the following data attributes to change styles based on current status:

  • data-loading – when loading prop is true
  • data-accept – when user drags files that can be accepted over the dropzone
  • data-reject – when user drags files that cannot be accepted over the dropzone
  • data-idle – default state – user does not drag any files over dropzone
import { Text, rem } from '@mantine/core';
import { Dropzone, IMAGE_MIME_TYPE } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone
onDrop={() => {}}
accept={IMAGE_MIME_TYPE}
sx={(theme) => ({
minHeight: rem(120),
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
border: 0,
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
'&[data-accept]': {
color: theme.white,
backgroundColor: theme.colors.blue[6],
},
'&[data-reject]': {
color: theme.white,
backgroundColor: theme.colors.red[6],
},
})}
>
<Text align="center">Drop images here</Text>
</Dropzone>
);
}

Images previews

import { useState } from 'react';
import { Text, Image, SimpleGrid } from '@mantine/core';
import { Dropzone, IMAGE_MIME_TYPE, FileWithPath } from '@mantine/dropzone';
function Demo() {
const [files, setFiles] = useState<FileWithPath[]>([]);
const previews = files.map((file, index) => {
const imageUrl = URL.createObjectURL(file);
return (
<Image
key={index}
src={imageUrl}
imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }}
/>
);
});
return (
<div>
<Dropzone accept={IMAGE_MIME_TYPE} onDrop={setFiles}>
<Text align="center">Drop images here</Text>
</Dropzone>
<SimpleGrid
cols={4}
breakpoints={[{ maxWidth: 'sm', cols: 1 }]}
mt={previews.length > 0 ? 'xl' : 0}
>
{previews}
</SimpleGrid>
</div>
);
}

Get ref

import { useRef, useEffect } from 'react';
import { Dropzone } from '@mantine/dropzone';
function Demo() {
const dropzoneRef = useRef<HTMLDivElement>(null);
useEffect(() => {
dropzoneRef.current.focus();
}, []);
return <Dropzone ref={dropzoneRef}>{/* children */}</Dropzone>;
}

Dropzone.FullScreen component

Dropzone.FullScreen lets you capture files dropped to browser window instead of specific area. It supports the same props as Dropzone component.

To preview component click button and drop images to browser window:

import { useState } from 'react';
import { Group, Text, useMantineTheme, Button } from '@mantine/core';
import { IconUpload, IconPhoto, IconX } from '@tabler/icons-react';
import { Dropzone, DropzoneProps, IMAGE_MIME_TYPE } from '@mantine/dropzone';
function Demo() {
const [active, setActive] = useState(false);
const theme = useMantineTheme();
return (
<>
<Group position="center">
<Button color={active ? 'red' : 'blue'} onClick={() => setActive((d) => !d)}>
{active ? 'Disable' : 'Enable'} full screen dropzone
</Button>
</Group>
<Dropzone.FullScreen
active={active}
accept={IMAGE_MIME_TYPE}
onDrop={(files) => {
console.log(files);
setActive(false);
}}
>
<Group position="center" spacing="xl" mih={220} sx={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload
size="3.2rem"
stroke={1.5}
color={theme.colors[theme.primaryColor][theme.colorScheme === 'dark' ? 4 : 6]}
/>
</Dropzone.Accept>
<Dropzone.Reject>
<IconX
size="3.2rem"
stroke={1.5}
color={theme.colors.red[theme.colorScheme === 'dark' ? 4 : 6]}
/>
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size="3.2rem" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" color="dimmed" inline mt={7}>
Attach as many files as you like, each file should not exceed 5mb
</Text>
</div>
</Group>
</Dropzone.FullScreen>
</>
);
}

Dropzone component props

NameTypeDescription
accept
string[] | Accept
File types to accept
activateOnClick
boolean
If false, disables click to open the native file selection dialog
activateOnDrag
boolean
If false, disables drag 'n' drop
activateOnKeyboard
boolean
If false, disables Space/Enter to open the native file selection dialog. Note that it also stops tracking the focus state.
autoFocus
boolean
Set to true to autofocus the root element
children *
ReactNode
Dropzone statues
disabled
boolean
Disable files capturing
dragEventsBubbling
boolean
If false, stops drag event propagation to parents
getFilesFromEvent
(event: DropEvent) => Promise<(File | DataTransferItem)[]>
Use this to provide a custom file aggregator
loading
boolean
Display loading overlay over dropzone
maxFiles
number
Number of files that user can pick
maxSize
number
Set maximum file size in bytes
multiple
boolean
Allow selection of multiple files
name
string
Name of the form control. Submitted with the form as part of a name/value pair.
onDragEnter
(event: DragEvent<HTMLElement>) => void
Called when the `dragenter` event occurs
onDragLeave
(event: DragEvent<HTMLElement>) => void
Called when the `dragleave` event occurs
onDragOver
(event: DragEvent<HTMLElement>) => void
Called when the `dragover` event occurs
onDrop *
(files: FileWithPath[]) => void
Called when valid files are dropped into dropzone
onDropAny
(files: FileWithPath[], fileRejections: FileRejection[]) => void
Called when any files are dropped into dropzone
onFileDialogCancel
() => void
Called when user closes the file selection dialog with no selection
onFileDialogOpen
() => void
Called when user opens the file selection dialog
onReject
(fileRejections: FileRejection[]) => void
Called when selected files don't meet file restrictions
openRef
ForwardedRef<() => void>
Get open function as ref
padding
number | "xs" | "sm" | "md" | "lg" | "xl"
Padding from theme.spacing, or any valid CSS value to set padding
preventDropOnDocument
boolean
If false, allow dropped items to take over the current browser window
radius
number | "xs" | "sm" | "md" | "lg" | "xl"
Key of theme.radius or any valid CSS value to set border-radius, theme.defaultRadius by default
useFsAccessApi
boolean
Set to true to use the File System Access API to open the file picker instead of using an <input type="file"> click event, defaults to true
validator
<T extends File>(file: T) => FileError | FileError[]
Custom validation function. It must return null if there's no errors.

Dropzone.FullScreen component props

NameTypeDescription
accept
string[] | Accept
File types to accept
activateOnClick
boolean
If false, disables click to open the native file selection dialog
activateOnDrag
boolean
If false, disables drag 'n' drop
activateOnKeyboard
boolean
If false, disables Space/Enter to open the native file selection dialog. Note that it also stops tracking the focus state.
active
boolean
Determines whether user can drop files to browser window, true by default
autoFocus
boolean
Set to true to autofocus the root element
children *
ReactNode
Dropzone statues
disabled
boolean
Disable files capturing
dragEventsBubbling
boolean
If false, stops drag event propagation to parents
getFilesFromEvent
(event: DropEvent) => Promise<(File | DataTransferItem)[]>
Use this to provide a custom file aggregator
loading
boolean
Display loading overlay over dropzone
maxFiles
number
Number of files that user can pick
maxSize
number
Set maximum file size in bytes
multiple
boolean
Allow selection of multiple files
name
string
Name of the form control. Submitted with the form as part of a name/value pair.
onDragEnter
(event: DragEvent<HTMLElement>) => void
Called when the `dragenter` event occurs
onDragLeave
(event: DragEvent<HTMLElement>) => void
Called when the `dragleave` event occurs
onDragOver
(event: DragEvent<HTMLElement>) => void
Called when the `dragover` event occurs
onDrop *
(files: FileWithPath[]) => void
Called when valid files are dropped into dropzone
onDropAny
(files: FileWithPath[], fileRejections: FileRejection[]) => void
Called when any files are dropped into dropzone
onFileDialogCancel
() => void
Called when user closes the file selection dialog with no selection
onFileDialogOpen
() => void
Called when user opens the file selection dialog
onReject
(fileRejections: FileRejection[]) => void
Called when selected files don't meet file restrictions
openRef
ForwardedRef<() => void>
Get open function as ref
padding
number | "xs" | "sm" | "md" | "lg" | "xl"
Padding from theme.spacing, or any valid CSS value to set padding
portalProps
Omit<PortalProps, "children" | "withinPortal">
Props to pass down to the portal when withinPortal is true
preventDropOnDocument
boolean
If false, allow dropped items to take over the current browser window
radius
number | "xs" | "sm" | "md" | "lg" | "xl"
Key of theme.radius or any valid CSS value to set border-radius, theme.defaultRadius by default
useFsAccessApi
boolean
Set to true to use the File System Access API to open the file picker instead of using an <input type="file"> click event, defaults to true
validator
<T extends File>(file: T) => FileError | FileError[]
Custom validation function. It must return null if there's no errors.
withinPortal
boolean
Determines whether component should be rendered within Portal, true by default
zIndex
ZIndex
z-index value, 9999 by default

Dropzone component Styles API

NameStatic selectorDescription
root.mantine-Dropzone-rootRoot element
inner.mantine-Dropzone-innerInner element containing dropzone children

DropzoneFullScreen component Styles API

NameStatic selectorDescription
root.mantine-DropzoneFullScreen-rootRoot element
inner.mantine-DropzoneFullScreen-innerInner element containing dropzone children
wrapper.mantine-DropzoneFullScreen-wrapperWrapper around dropzone