File Uploader
A composable, accessible FileUploader component for modern React applications.
tsx
import { FileUploader } from '@nofinite/nui';Interactive Preview
Loading preview...
FileUploader is a flexible file upload component that supports drag-and-drop, click-to-upload, keyboard interaction, file previews, and file removal. It works for both single and multiple file uploads with optional file type filtering.
Usage
Basic usage
tsx
import { FileUploader } from '@nofinite/nui';
<FileUploader onChange={(files) => console.log(files)} />;Multiple files
tsx
<FileUploader multiple />Controlled state
tsx
const [files, setFiles] = useState<File[]>([]);
<FileUploader value={files} onChange={setFiles} />;With restrictions
tsx
<FileUploader multiple accept="image/*" maxSize={2 * 1024 * 1024} />Disabled
tsx
<FileUploader disabled />Subcomponents
None exposed.
Variants
tsx
<FileUploader />
<FileUploader multiple />
<FileUploader disabled />
<FileUploader accept="image/*" />
<FileUploader maxSize={2097152} />Available variants:
- single (default)
- multiple
- disabled
- type-restricted
- size-restricted
Guidelines:
- Use multiple for gallery or document uploads
- Always provide accept when backend enforces MIME
- Use size restriction to avoid unnecessary network cost
Sizes
Layout behavior:
| Element | Behavior |
|---|---|
| Dropzone | full-width container |
| File list | vertical stack |
| File item | auto height with truncation |
| Icons | fixed (16–24px) |
Sizing is controlled via layout utilities and token spacing.
Shapes / Modes
Interaction states
- Idle
- Drag-over
- File-selected
- File-removed
- Disabled
Data modes
- Controlled
- Uncontrolled
- Multi-file aggregation
- Size-filtered
- MIME-filtered (native)
Accessibility
Implemented:
- Dropzone
role="button"- keyboard activation (Enter/Space)
- focus-visible outline
aria-disabled- drag event semantics
- Remove button
- aria-label per file
- Hidden input remains native for browser accessibility
- Programmatic focus preserved
Limitations:
- No upload progress semantics
- No aria-live updates for file list
- No error announcement for rejected files
- Drag announcements depend on browser behavior
Animation model
- File item pop-in via translateY + fade
- Duration: 200ms
- Drag-over visual highlight
- Motion disabled with
prefers-reduced-motion
No exit animation for removal.
Architectural notes
Key design decisions:
- Input proxy pattern
- Controlled/uncontrolled duality
- Immutable file list replacement
- Size validation pre-merge
- Stable file key generation using name + size + lastModified
- Drag state isolation
- Event propagation control
Potential caveat:
- File object identity used for removal (same file reselect handled via input reset)
Best practices
Do
- Validate server-side regardless of client filters
- Provide accept to guide user selection
- Use multiple for batch uploads
- Display upload progress externally if needed
Don’t
- Depend on client MIME filtering for security
- Upload extremely large files without chunking
- Store File objects long-term in global state
- Render heavy previews synchronously
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
value | File[] | — | Controlled state for the selected files |
defaultValue | File[] | — | Uncontrolled initial state for the selected files |
onChange | ((files: File[]) => void) | — | Callback fired when the list of selected files changes |
multiple | boolean | false | Whether to allow multiple files to be selected. Defaults to false. |
accept | string | — | A comma-separated list of allowed file extensions or MIME types (e.g., '.jpg, .png, application/pdf') |
maxSize | number | — | Maximum allowed file size in bytes |
className | string | — | Custom class name applied to the root container |
placeholder | ReactNode | — | Custom text or element displayed inside the dropzone (Primary Title) |
description | ReactNode | — | Secondary text or description below the placeholder |
disabled | boolean | false | Disables the dropzone and prevents file selection |