Welcome folks today in this blog post we will be using the react-dropzone
library to drag and drop
multiple files with preview
example and validation
in browser using javascript. All the full source code of the application is shown below.
Get Started
In order to get started you need to make a new react.js
project using the below command as shown below
npx create-react-app sampleapp
cd sampleapp
npm i react-dropzone
And after that you will see the below directory
structure of the react.js app as shown below
Drag and Drop Multiple Files Upload
Now we need to modify the App.js
file and copy paste the following code
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import React from 'react'; import {useDropzone} from 'react-dropzone'; export default function App() { const {acceptedFiles, getRootProps, getInputProps} = useDropzone(); const files = acceptedFiles.map(file => ( <li key={file.path}> {file.path} - {file.size} bytes </li> )); return ( <section className="container"> <div {...getRootProps({className: 'dropzone'})}> <input {...getInputProps()} /> <p>Drag 'n' drop some files here, or click to select files</p> </div> <aside> <h4>Files</h4> <ul>{files}</ul> </aside> </section> ); } |
As you can see we are importing the react-dropzone
library and then we are rendering the drag and drop
widget inside the browser and the user can drag
the files which needs to be uploaded. And then we are showing the file
size and the path as shown below
Accepting Only Certain Files
Now we can pass a config
object to the react dropzone and here we can accept only specific file
extension which is png
and html
as shown below
1 2 3 4 5 6 |
useDropzone({ accept: { 'image/png': ['.png'], 'text/html': ['.html', '.htm'], } }); |
Validating the Max File Size and No of Files
You can even control how many max
files you can select for uploading and also you can control the maximum size
of the file to upload as shown below
1 2 3 4 5 6 7 8 |
useDropzone({ accept: { 'image/png': ['.png'], 'text/html': ['.html', '.htm'], }, maxFiles:3, maxSize:500000 }); |
Custom Validation Message
Now we can even pass a custom validation
message to the react-dropzone
by using the validator
property as shown below
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
import React from 'react'; import {useDropzone} from 'react-dropzone'; const maxLength = 5; function nameLengthValidator(file) { if (file.name.length > maxLength) { return { code: "name-too-large", message: `Name is larger than ${maxLength} characters` }; } return null } export default function App(props) { const { acceptedFiles, fileRejections, getRootProps, getInputProps } = useDropzone({ validator: nameLengthValidator }); const acceptedFileItems = acceptedFiles.map(file => ( <li key={file.path}> {file.path} - {file.size} bytes </li> )); const fileRejectionItems = fileRejections.map(({ file, errors }) => ( <li key={file.path}> {file.path} - {file.size} bytes <ul> {errors.map(e => ( <li key={e.code}>{e.message}</li> ))} </ul> </li> )); return ( <section className="container"> <div {...getRootProps({ className: 'dropzone' })}> <input {...getInputProps()} /> <p>Drag 'n' drop some files here, or click to select files</p> <em>(Only files with name less than 20 characters will be accepted)</em> </div> <aside> <h4>Accepted files</h4> <ul>{acceptedFileItems}</ul> <h4>Rejected files</h4> <ul>{fileRejectionItems}</ul> </aside> </section> ); } |
As you can see we are defining the custom validator
function in which we are checking the name
of the file and we are checking the length
of the file. And we have given the maxLength
for the characters is 5. And then we are showing the error
message if the name of the file is larger than 5 as shown below
Open File Dialog on Button Click
Now we will be opening file dialog
on button click in react.js using the react-dropzone
library as shown below
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import React from 'react'; import {useDropzone} from 'react-dropzone'; export default function App(props) { const {getRootProps, getInputProps, open, acceptedFiles} = useDropzone({ // Disable click and keydown behavior noClick: true, noKeyboard: true }); const files = acceptedFiles.map(file => ( <li key={file.path}> {file.path} - {file.size} bytes </li> )); return ( <div className="container"> <div {...getRootProps({className: 'dropzone'})}> <input {...getInputProps()} /> <p>Drag 'n' drop some files here</p> <button type="button" onClick={open}> Open File Dialog </button> </div> <aside> <h4>Files</h4> <ul>{files}</ul> </aside> </div> ); } |
As you can see we are having a button and inside this we are attaching the onClick
attribute where we are executing the open()
method and inside it we are passing the onclick
and onkeyboard
boolean attribute to true.
Showing Preview of Images
Now we can also see the live
preview of the files that you select using the react-dropzone
library as shown below
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
import React, {useEffect, useState} from 'react'; import {useDropzone} from 'react-dropzone'; const thumbsContainer = { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', marginTop: 16 }; const thumb = { display: 'inline-flex', borderRadius: 2, border: '1px solid #eaeaea', marginBottom: 8, marginRight: 8, width: 100, height: 100, padding: 4, boxSizing: 'border-box' }; const thumbInner = { display: 'flex', minWidth: 0, overflow: 'hidden' }; const img = { display: 'block', width: 'auto', height: '100%' }; export default function App(props) { 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 => ( <div style={thumb} key={file.name}> <div style={thumbInner}> <img src={file.preview} style={img} // Revoke data uri after image is loaded onLoad={() => { URL.revokeObjectURL(file.preview) }} /> </div> </div> )); useEffect(() => { // Make sure to revoke the data uris to avoid memory leaks, will run on unmount return () => files.forEach(file => URL.revokeObjectURL(file.preview)); }, []); return ( <section className="container"> <div {...getRootProps({className: 'dropzone'})}> <input {...getInputProps()} /> <p>Drag 'n' drop some files here, or click to select files</p> </div> <aside style={thumbsContainer}> {thumbs} </aside> </section> ); } |