useFileDialog()
Last updated: 24/04/2026
Overview
useFileDialog lazily creates a hidden <input type="file">, wires its change event to React state, and gives you imperative open / reset helpers so you can trigger the native picker from any button without embedding an input in your markup. Options map directly to that input: accept filters MIME types or extensions, multiple allows multi-select, and capture hints camera vs gallery on mobile. After selection, files is a File[] you can read with FileReader, upload, or validate; reset clears the input value and empties files so the same file can be chosen again.
What it accepts
- Optional
accept- Passed toinput.accept(for exampleimage/*or.png,.jpg). - Optional
multiple- Whentrue, the user can pick more than one file. Defaultfalse. - Optional
capture- Forwarded as the inputcaptureattribute (for exampleuser/environmenton supported devices).
What it returns
files- Selected files from the lastchangeevent (File[]).open- Programmatically opens the file dialog (click()on the hidden input).reset- Clears the input value and resetsfilesto[].
Usage
Picker for several images with an accept mask; list chosen names and clear the selection.
import useFileDialog from '@dedalik/use-react/useFileDialog'
function Example() {
const { files, open, reset } = useFileDialog({
accept: 'image/png,image/jpeg,image/webp',
multiple: true,
capture: 'environment',
})
return (
<div>
<h3>Images</h3>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 12 }}>
<button type='button' onClick={open}>
Choose files
</button>
<button type='button' onClick={reset}>
Clear
</button>
</div>
{files.length === 0 ? (
<p>No files selected.</p>
) : (
<ul>
{files.map((file) => (
<li key={`${file.name}-${file.size}`}>
<strong>{file.name}</strong> - {(file.size / 1024).toFixed(1)} KB
</li>
))}
</ul>
)}
</div>
)
}
export default function Demo() {
return <Example />
}API Reference
useFileDialog
Signature: useFileDialog(options?: UseFileDialogOptions): UseFileDialogReturn
Parameters
Optional options (UseFileDialogOptions):
accept(string, optional) -input.acceptfilter.multiple(boolean, optional) - Multi-file selection. Defaultfalse.capture(string, optional) -captureattribute on the hidden input.
Returns
Object with:
files- Latest selection asFile[].open- Opens the native file chooser (() => void).reset- Clears value andfiles(() => void).
Copy-paste hook
TypeScript
import { useCallback, useMemo, useRef, useState } from 'react'
export interface UseFileDialogOptions {
accept?: string
multiple?: boolean
capture?: string
}
export interface UseFileDialogReturn {
files: File[]
open: () => void
reset: () => void
}
/**
* Opens a hidden file input and exposes selected files.
*/
export default function useFileDialog(options: UseFileDialogOptions = {}): UseFileDialogReturn {
const { accept, multiple = false, capture } = options
const inputRef = useRef<HTMLInputElement | null>(null)
const [files, setFiles] = useState<File[]>([])
const ensureInput = useCallback(() => {
if (inputRef.current) return inputRef.current
const input = document.createElement('input')
input.type = 'file'
if (accept) input.accept = accept
input.multiple = multiple
if (capture) input.setAttribute('capture', capture)
input.style.display = 'none'
input.addEventListener('change', () => {
setFiles(Array.from(input.files ?? []))
})
document.body.appendChild(input)
inputRef.current = input
return input
}, [accept, capture, multiple])
const open = useCallback(() => {
if (typeof document === 'undefined') return
ensureInput().click()
}, [ensureInput])
const reset = useCallback(() => {
const input = inputRef.current
if (!input) return
input.value = ''
setFiles([])
}, [])
return useMemo(() => ({ files, open, reset }), [files, open, reset])
}JavaScript
import { useCallback, useMemo, useRef, useState } from 'react'
export default function useFileDialog(options = {}) {
const { accept, multiple = false, capture } = options
const inputRef = useRef(null)
const [files, setFiles] = useState([])
const ensureInput = useCallback(() => {
if (inputRef.current) return inputRef.current
const input = document.createElement('input')
input.type = 'file'
if (accept) input.accept = accept
input.multiple = multiple
if (capture) input.setAttribute('capture', capture)
input.style.display = 'none'
input.addEventListener('change', () => {
setFiles(Array.from(input.files ?? []))
})
document.body.appendChild(input)
inputRef.current = input
return input
}, [accept, capture, multiple])
const open = useCallback(() => {
if (typeof document === 'undefined') return
ensureInput().click()
}, [ensureInput])
const reset = useCallback(() => {
const input = inputRef.current
if (!input) return
input.value = ''
setFiles([])
}, [])
return useMemo(() => ({ files, open, reset }), [files, open, reset])
}