Installations
npm install use-file-picker
Score
93.7
Supply Chain
98
Quality
80.5
Maintenance
100
Vulnerability
100
License
Developer
Developer Guide
Module System
ESM
Min. Node Version
>=12
Typescript Support
Yes
Node Version
20.11.1
NPM Version
10.2.4
Statistics
281 Stars
137 Commits
34 Forks
4 Watching
21 Branches
14 Contributors
Updated on 22 Nov 2024
Languages
TypeScript (98.26%)
JavaScript (1.19%)
HTML (0.55%)
Total Downloads
Cumulative downloads
Total Downloads
2,282,029
Last day
-7.2%
4,510
Compared to previous day
Last week
-8.5%
21,949
Compared to previous week
Last month
5.1%
103,427
Compared to previous month
Last year
42.9%
1,076,605
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Dependencies
1
Peer Dependencies
1
Dev Dependencies
26
Welcome to use-file-picker 👋
Simple react hook to open browser file selector.
🏠 Homepage
Documentation
Install
npm i use-file-picker
or
yarn add use-file-picker
Usage
Simple txt file content reading
1import { useFilePicker } from 'use-file-picker'; 2import React from 'react'; 3 4export default function App() { 5 const { openFilePicker, filesContent, loading } = useFilePicker({ 6 accept: '.txt', 7 }); 8 9 if (loading) { 10 return <div>Loading...</div>; 11 } 12 13 return ( 14 <div> 15 <button onClick={() => openFilePicker()}>Select files</button> 16 <br /> 17 {filesContent.map((file, index) => ( 18 <div key={index}> 19 <h2>{file.name}</h2> 20 <div key={index}>{file.content}</div> 21 <br /> 22 </div> 23 ))} 24 </div> 25 ); 26}
Reading and rendering Images
1import { useFilePicker } from 'use-file-picker'; 2import { 3 FileAmountLimitValidator, 4 FileTypeValidator, 5 FileSizeValidator, 6 ImageDimensionsValidator, 7} from 'use-file-picker/validators'; 8 9export default function App() { 10 const { openFilePicker, filesContent, loading, errors } = useFilePicker({ 11 readAs: 'DataURL', 12 accept: 'image/*', 13 multiple: true, 14 validators: [ 15 new FileAmountLimitValidator({ max: 1 }), 16 new FileTypeValidator(['jpg', 'png']), 17 new FileSizeValidator({ maxFileSize: 50 * 1024 * 1024 /* 50 MB */ }), 18 new ImageDimensionsValidator({ 19 maxHeight: 900, // in pixels 20 maxWidth: 1600, 21 minHeight: 600, 22 minWidth: 768, 23 }), 24 ], 25 }); 26 27 if (loading) { 28 return <div>Loading...</div>; 29 } 30 31 if (errors.length) { 32 return <div>Error...</div>; 33 } 34 35 return ( 36 <div> 37 <button onClick={() => openFilePicker()}>Select files </button> 38 <br /> 39 {filesContent.map((file, index) => ( 40 <div key={index}> 41 <h2>{file.name}</h2> 42 <img alt={file.name} src={file.content}></img> 43 <br /> 44 </div> 45 ))} 46 </div> 47 ); 48}
On change callbacks
1import { useFilePicker } from 'use-file-picker'; 2 3export default function App() { 4 const { openFilePicker, filesContent, loading, errors } = useFilePicker({ 5 readAs: 'DataURL', 6 accept: 'image/*', 7 multiple: true, 8 onFilesSelected: ({ plainFiles, filesContent, errors }) => { 9 // this callback is always called, even if there are errors 10 console.log('onFilesSelected', plainFiles, filesContent, errors); 11 }, 12 onFilesRejected: ({ errors }) => { 13 // this callback is called when there were validation errors 14 console.log('onFilesRejected', errors); 15 }, 16 onFilesSuccessfullySelected: ({ plainFiles, filesContent }) => { 17 // this callback is called when there were no validation errors 18 console.log('onFilesSuccessfullySelected', plainFiles, filesContent); 19 }, 20 }); 21 22 if (loading) { 23 return <div>Loading...</div>; 24 } 25 26 if (errors.length) { 27 return <div>Error...</div>; 28 } 29 30 return ( 31 <div> 32 <button onClick={() => openFilePicker()}>Select files </button> 33 <br /> 34 {filesContent.map((file, index) => ( 35 <div key={index}> 36 <h2>{file.name}</h2> 37 <img alt={file.name} src={file.content}></img> 38 <br /> 39 </div> 40 ))} 41 </div> 42 ); 43}
Advanced usage
1import { useFilePicker } from 'use-file-picker'; 2import React from 'react'; 3 4export default function App() { 5 const { openFilePicker, filesContent, loading, errors, plainFiles, clear } = useFilePicker({ 6 multiple: true, 7 readAs: 'DataURL', // availible formats: "Text" | "BinaryString" | "ArrayBuffer" | "DataURL" 8 // accept: '.ics,.pdf', 9 accept: ['.json', '.pdf'], 10 validators: [new FileAmountLimitValidator({ min: 2, max: 3 })], 11 // readFilesContent: false, // ignores file content 12 }); 13 14 if (errors.length) { 15 return ( 16 <div> 17 <button onClick={() => openFilePicker()}>Something went wrong, retry! </button> 18 {errors.map(err => ( 19 <div> 20 {err.name}: {err.reason} 21 /* e.g. "name":"FileAmountLimitError", "reason":"MAX_AMOUNT_OF_FILES_EXCEEDED" */ 22 </div> 23 ))} 24 </div> 25 ); 26 } 27 28 if (loading) { 29 return <div>Loading...</div>; 30 } 31 32 return ( 33 <div> 34 <button onClick={() => openFilePicker()}>Select file </button> 35 <button onClick={() => clear()}>Clear</button> 36 <br /> 37 Number of selected files: 38 {plainFiles.length} 39 <br /> 40 {/* If readAs is set to DataURL, You can display an image */} 41 {!!filesContent.length && <img src={filesContent[0].content} />} 42 <br /> 43 {plainFiles.map(file => ( 44 <div key={file.name}>{file.name}</div> 45 ))} 46 </div> 47 ); 48}
Callbacks
You can hook your logic into callbacks that will be fired at specific events during the lifetime of the hook. useFilePicker accepts these callbacks:
- onFilesSelected
- onFilesRejected
- onFilesSuccessfullySelected
- onClear
These are described in more detail in the Props section.
1import { useFilePicker } from 'use-file-picker'; 2 3export default function App() { 4 const { openFilePicker, filesContent, loading, errors, plainFiles, clear } = useFilePicker({ 5 multiple: true, 6 readAs: 'DataURL', 7 accept: ['.json', '.pdf'], 8 onFilesSelected: ({ plainFiles, filesContent, errors }) => { 9 // this callback is always called, even if there are errors 10 console.log('onFilesSelected', plainFiles, filesContent, errors); 11 }, 12 onFilesRejected: ({ errors }) => { 13 // this callback is called when there were validation errors 14 console.log('onFilesRejected', errors); 15 }, 16 onFilesSuccessfullySelected: ({ plainFiles, filesContent }) => { 17 // this callback is called when there were no validation errors 18 console.log('onFilesSuccessfullySelected', plainFiles, filesContent); 19 }, 20 onClear: () => { 21 // this callback is called when the selection is cleared 22 console.log('onClear'); 23 }, 24 }); 25}
useImperativePicker
hook also accepts the callbacks listed above. Additionally, it accepts the onFileRemoved
callback, which is called when a file is removed from the list of selected files.
1import { useImperativeFilePicker } from 'use-file-picker'; 2 3export default function App() { 4 const { openFilePicker, filesContent, loading, errors, plainFiles, clear } = useImperativeFilePicker({ 5 onFileRemoved: (removedFile, removedIndex) => { 6 // this callback is called when a file is removed from the list of selected files 7 console.log('onFileRemoved', removedFile, removedIndex); 8 }, 9 }); 10}
Keeping the previously selected files and removing them from selection on demand
If you want to keep the previously selected files and remove them from the selection, you can use a separate hook called useImperativeFilePicker
that is also exported in this package. For files removal, you can use removeFileByIndex
or removeFileByReference
functions.
1import React from 'react'; 2import { useImperativeFilePicker } from 'use-file-picker'; 3 4const Imperative = () => { 5 const { openFilePicker, filesContent, loading, errors, plainFiles, clear, removeFileByIndex, removeFileByReference } = 6 useImperativeFilePicker({ 7 multiple: true, 8 readAs: 'Text', 9 readFilesContent: true, 10 }); 11 12 if (errors.length) { 13 return ( 14 <div> 15 <button onClick={() => openFilePicker()}>Something went wrong, retry! </button> 16 <div style={{ display: 'flex', flexDirection: 'column' }}> 17 {console.log(errors)} 18 {errors.map(err => ( 19 <div> 20 {err.name}: {err.reason} 21 /* e.g. "name":"FileAmountLimitError", "reason":"MAX_AMOUNT_OF_FILES_EXCEEDED" */ 22 </div> 23 ))} 24 </div> 25 </div> 26 ); 27 } 28 29 if (loading) { 30 return <div>Loading...</div>; 31 } 32 33 return ( 34 <div> 35 <button onClick={async () => openFilePicker()}>Select file</button> 36 <button onClick={() => clear()}>Clear</button> 37 <br /> 38 Amount of selected files: 39 {plainFiles.length} 40 <br /> 41 Amount of filesContent: 42 {filesContent.length} 43 <br /> 44 {plainFiles.map((file, i) => ( 45 <div> 46 <div style={{ display: 'flex', alignItems: 'center' }} key={file.name}> 47 <div>{file.name}</div> 48 <button style={{ marginLeft: 24 }} onClick={() => removeFileByReference(file)}> 49 Remove by reference 50 </button> 51 <button style={{ marginLeft: 24 }} onClick={() => removeFileByIndex(i)}> 52 Remove by index 53 </button> 54 </div> 55 <div>{filesContent[i]?.content}</div> 56 </div> 57 ))} 58 </div> 59 ); 60};
API
Props
Prop name | Description | Default value | Example values |
---|---|---|---|
multiple | Allow user to pick multiple files at once | true | true, false |
accept | Set type of files that user can choose from the list | "*" | [".png", ".txt"], "image/*", ".txt" |
readAs | Set a return type of filesContent | "Text" | "DataURL", "Text", "BinaryString", "ArrayBuffer" |
readFilesContent | Ignores files content and omits reading process if set to false | true | true, false |
validators | Add validation logic. You can use some of the built-in validators like FileAmountLimitValidator or create your own custom validation logic | [] | [MyValidator, MySecondValidator] |
initializeWithCustomParameters | allows for customization of the input element that is created by the file picker. It accepts a function that takes in the input element as a parameter and can be used to set any desired attributes or styles on the element. | n/a | (input) => input.setAttribute("disabled", "") |
onFilesSelected | A callback function that is called when files are successfully selected. The function is passed an array of objects with information about each successfully selected file | n/a | (data) => console.log(data) |
onFilesSuccessfullySelected | A callback function that is called when files are successfully selected. The function is passed an array of objects with information about each successfully selected file | n/a | (data) => console.log(data) |
onFilesRejected | A callback function that is called when files are rejected due to validation errors or other issues. The function is passed an array of objects with information about each rejected file | n/a | (data) => console.log(data) |
onClear | A callback function that is called when the selection is cleared. | n/a | () => console.log('selection cleared') |
Returns
Name | Description |
---|---|
openFilePicker | Opens file selector |
clear | Clears all files and errors |
filesContent | Get files array of type FileContent |
plainFiles | Get array of the File objects |
loading | True if the reading files is in progress, otherwise False |
errors | Get errors array of type FileError if any appears |
Built-in validators
useFilePicker has some built-in validators that can be used out of the box. These are:
- FileAmountLimitValidator - allows to select a specific number of files (min and max) that will pass validation. This will work great with simple useFilePicker use cases, it will run on every file selection.
- FileTypeValidator - allows to select files with a specific extension that will pass validation.
- FileSizeValidator - allows to select files of a specific size (min and max) in bytes that will pass validation.
- ImageDimensionsValidator - allows to select images of a specific size (min and max) that will pass validation.
- PersistentFileAmountLimitValidator - allows to select a specific number of files (min and max) that will pass validation but it takes into consideration the previously selected files. This will work great with useImperativeFilePicker hook when you want to keep track of how many files are selected even when user is allowed to trigger selection multiple times before submitting the files.
Custom validation
useFilePicker allows for injection of custom validation logic. Validation is divided into two steps:
- validateBeforeParsing - takes place before parsing. You have access to config passed as argument to useFilePicker hook and all plain file objects of selected files by user. Called once for all files after selection.
- validateAfterParsing - takes place after parsing (or is never called if readFilesContent is set to false).You have access to config passed as argument to useFilePicker hook, FileWithPath object that is currently being validated and the reader object that has loaded that file. Called individually for every file.
1interface Validator { 2 validateBeforeParsing(config: UseFilePickerConfig, plainFiles: File[]): Promise<void>; 3 validateAfterParsing(config: UseFilePickerConfig, file: FileWithPath, reader: FileReader): Promise<void>; 4}
Validators must return Promise object - resolved promise means that file passed validation, rejected promise means that file did not pass.
Since version 2.0, validators also have optional callback handlers that will be run when an important event occurs during the selection process. These are:
1 /** 2 * lifecycle method called after user selection (regardless of validation result) 3 */ 4 onFilesSelected( 5 _data: SelectedFilesOrErrors<ExtractContentTypeFromConfig<ConfigType>, CustomErrors> 6 ): Promise<void> | void {} 7 /** 8 * lifecycle method called after successful validation 9 */ 10 onFilesSuccessfullySelected(_data: SelectedFiles<ExtractContentTypeFromConfig<ConfigType>>): Promise<void> | void {} 11 /** 12 * lifecycle method called after failed validation 13 */ 14 onFilesRejected(_data: FileErrors<CustomErrors>): Promise<void> | void {} 15 /** 16 * lifecycle method called after the selection is cleared 17 */ 18 onClear(): Promise<void> | void {} 19 20 /** 21 * This method is called when file is removed from the list of selected files. 22 * Invoked only by the useImperativeFilePicker hook 23 * @param _removedFile removed file 24 * @param _removedIndex index of removed file 25 */ 26 onFileRemoved(_removedFile: File, _removedIndex: number): Promise<void> | void {}
Example validator
1/** 2 * validateBeforeParsing allows the user to select only an even number of files 3 * validateAfterParsing allows the user to select only files that have not been modified in the last 24 hours 4 */ 5class CustomValidator extends Validator { 6 async validateBeforeParsing(config: ConfigType, plainFiles: File) { 7 return new Promise<void>((res, rej) => (plainFiles.length % 2 === 0 ? res() : rej({ oddNumberOfFiles: true }))); 8 } 9 async validateAfterParsing(config: ConfigType, file: FileWithPath, reader: FileReader) { 10 return new Promise<void>((res, rej) => 11 file.lastModified < new Date().getTime() - 24 * 60 * 60 * 1000 12 ? res() 13 : rej({ fileRecentlyModified: true, lastModified: file.lastModified }) 14 ); 15 } 16 onFilesSuccessfullySelected(data: SelectedFiles<ExtractContentTypeFromConfig<ConfigType>>) { 17 console.log(data); 18 } 19}
Authors
👤 Milosz Jankiewicz
- Twitter: @twitter.com/jaaneek/
- Github: @Jaaneek
- LinkedIn: @https://www.linkedin.com/in/jaaneek
👤 Kamil Planer
- Github: @MrKampla
- LinkedIn: @https://www.linkedin.com/in/kamil-planer/
👤 Adam Dobrzeniewski
- Twitter: @twitter.com/xForsect
- Github: @Forsect
- LinkedIn: @https://www.linkedin.com/in/adam-dobrzeniewski
🤝 Contributing
Contributions, issues and feature requests are welcome!
Feel free to check the issues page.
Show your support
Give a ⭐️ if this project helped you!
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
Found 14/18 approved changesets -- score normalized to 7
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/main.yml:1
- Warn: no topLevel permission defined: .github/workflows/size.yml:1
- Info: no jobLevel write permissions found
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/Jaaneek/useFilePicker/main.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/Jaaneek/useFilePicker/main.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/Jaaneek/useFilePicker/main.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/size.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/Jaaneek/useFilePicker/size.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/size.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/Jaaneek/useFilePicker/size.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/size.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/Jaaneek/useFilePicker/size.yml/master?enable=pin
- Warn: npmCommand not pinned by hash: .github/workflows/main.yml:27
- Info: 0 out of 4 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 2 third-party GitHubAction dependencies pinned
- Info: 0 out of 1 npmCommand dependencies pinned
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 27 are checked with a SAST tool
Reason
46 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j
- Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-c24v-8rfc-w8vw
- Warn: Project is vulnerable to: GHSA-8jhw-289h-jh2g
- Warn: Project is vulnerable to: GHSA-4vvj-4cpr-p986 / GHSA-64vr-g452-qvp3
- Warn: Project is vulnerable to: GHSA-9cwx-2883-4wfx
- Warn: Project is vulnerable to: GHSA-hpx4-r86g-5jrg
- Warn: Project is vulnerable to: GHSA-prr3-c3m5-p7q2
- Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw
- Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-434g-2637-qmqr
- Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m
- Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw
- Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p
- Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747
- Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc
- Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx
- Warn: Project is vulnerable to: GHSA-vfrc-7r7c-w9mx
- Warn: Project is vulnerable to: GHSA-7wwv-vh3v-89cq
- Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22
- Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp
- Warn: Project is vulnerable to: GHSA-4wx3-54gh-9fr9
- Warn: Project is vulnerable to: GHSA-xf5p-87ch-gxw2
- Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj
- Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g
- Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j
- Warn: Project is vulnerable to: GHSA-wvhm-4hhf-97x9
- Warn: Project is vulnerable to: GHSA-h4hr-7fg3-h35w
- Warn: Project is vulnerable to: GHSA-gj77-59wh-66hg
- Warn: Project is vulnerable to: GHSA-hqhp-5p83-hx96
- Warn: Project is vulnerable to: GHSA-3949-f494-cm99
- Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg
- Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p
- Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36
- Warn: Project is vulnerable to: GHSA-w5p7-h5w8-2hfq
- Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v
- Warn: Project is vulnerable to: GHSA-wr3j-pwj9-hqq6
- Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
- Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q
Score
3.5
/10
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to use-file-picker
vanilla-picker
A simple, easy to use vanilla JS color picker with alpha selection.
react-datepicker
A simple and reusable datepicker component for React
react-date-picker
A date picker for your React app.
react-native-image-crop-picker
Select single or multiple images, with cropping option