file-upload.tsx
Overview
The file-upload.tsx file provides a comprehensive, accessible, and customizable React component library for file uploading functionality. It supports drag-and-drop, file selection dialogs, file validation, progress tracking, and file metadata display. The library is designed with modular components that can be composed together or used individually, enabling flexible integration into React applications.
Key features include:
Controlled and uncontrolled file upload state management.
Drag-and-drop support with visual feedback.
File type, size, and count validation.
File upload progress tracking with multiple progress bar styles.
File preview with automatic image thumbnail generation or icon fallback.
Accessibility with ARIA attributes and keyboard support.
Flexible styling with
asChildprop for custom component wrappers.Context-based state management with React Context and a custom store.
Components and Utilities
1. FileUploadRoot
Purpose:
The root component that manages the overall file upload state and provides context to child components.
Props:
Prop | Type | Description |
|---|---|---|
| Controlled list of files. | |
Default files for uncontrolled usage. | ||
| Callback when the controlled file list changes. | |
| Called when files are accepted after validation. | |
Called for each individual accepted file. | ||
Called for each rejected file with a rejection message. | ||
[(file: File) => string \ | null \ | |
| [(files: File[], options: { onProgress, onSuccess, onError }) => Promise \ | void](/projects/311/72503) |
|
| Comma-separated list of accepted MIME types or extensions. |
|
| Maximum number of files allowed. |
|
| Maximum file size in bytes. |
| `'ltr' \ | 'rtl'` |
|
| Visually hidden label for input accessibility. |
|
| Name attribute for input. |
|
| Renders as child component wrapper (uses Radix UI's Slot). |
|
| Disables the upload functionality. |
|
| Marks the component as invalid (e.g., validation error). |
| Allows multiple file selection. | |
|
| Marks the input as required. |
Other props |
| Additional props for the root div element. |
Usage Example:
<FileUploadRoot
accept="image/*"
maxFiles={5}
maxSize={5 * 1024 * 1024}
onUpload={async (files, { onProgress, onSuccess, onError }) => {
for (const file of files) {
// simulate upload
for (let p = 0; p <= 100; p += 10) {
onProgress(file, p);
await new Promise(r => setTimeout(r, 100));
}
onSuccess(file);
}
}}
>
{/* Dropzone, List, etc. */}
</FileUploadRoot>
2. FileUploadDropzone
Purpose:
Area where users can drag and drop files, click to open file dialog, or paste files from clipboard.
Props:
Prop | Type | Description |
|---|---|---|
|
| Render as child component wrapper. |
Other props |
| Props forwarded to the container div. |
Features:
Handles drag events to show active drag state.
Clicking outside the trigger activates the file input.
Supports keyboard interaction (Enter or Space triggers file dialog).
Supports pasting files from clipboard.
3. FileUploadTrigger
Purpose:
A button or custom element that triggers the file selection dialog.
Props:
Prop | Type | Description |
|---|---|---|
|
| Render as child component wrapper. |
Other props |
| Props forwarded to the button element. |
4. FileUploadList
Purpose:
Renders the list of uploaded files.
Props:
Prop | Type | Description |
|---|---|---|
| `'horizontal' \ | 'vertical'` |
|
| Render as child component wrapper. |
|
| Forces rendering even if no files are present. |
5. FileUploadItem
Purpose:
Represents a single file item in the upload list, including status and accessibility info.
Props:
Prop | Type | Description |
|---|---|---|
|
| The file instance associated with this item. |
|
| Render as child component wrapper. |
6. FileUploadItemPreview
Purpose:
Renders a preview of the file, either an image thumbnail or a file type icon.
Props:
Prop | Type | Description |
|---|---|---|
|
| Custom render function to override default preview. |
|
| Render as child component wrapper. |
7. FileUploadItemMetadata
Purpose:
Displays metadata for a file such as name, size, and error message if any.
Props:
Prop | Type | Description |
|---|---|---|
|
| Render as child component wrapper. |
| `'default' \ | 'sm'` |
8. FileUploadItemProgress
Purpose:
Displays upload progress for an individual file.
Props:
Prop | Type | Description |
|---|---|---|
| `'linear' \ | 'circular' \ |
|
| Size in pixels for circular variant (default 40). |
|
| Render as child component wrapper. |
|
| Render even if progress is 100%. |
9. FileUploadItemDelete
Purpose:
Button to remove a file from the upload list.
Props:
Prop | Type | Description |
|---|---|---|
|
| Render as child component wrapper. |
10. FileUploadClear
Purpose:
Button to clear all files from the upload list.
Props:
Prop | Type | Description |
|---|---|---|
|
| Render even if no files are present. |
|
| Render as child component wrapper. |
|
| Explicitly disable button. |
Hooks and Utilities
useLazyRef<T>(fn: () => T): React.RefObject<T>
Creates a ref that initializes lazily once.
Parameters:
fn- initializer function returning the value to store in the ref.Returns:
A React ref object with the initialized value.
useDirection(dirProp?: Direction): Direction
Determines text direction ('ltr' or 'rtl') from context or prop.
useStore<T>(selector: (state: StoreState) => T): T
Subscribes to the internal store and selects a slice of the state.
Parameters:
selector- function to select part of store state.Returns:
The selected state slice, re-rendering component when it changes.
useStoreContext(consumerName: string)
Accesses the internal store context, ensuring it exists.
useFileUploadContext(consumerName: string)
Accesses the file upload context, providing shared IDs, refs, and flags.
formatBytes(bytes: number): string
Formats bytes into human-readable strings (e.g., "1.2 MB").
getFileIcon(file: File): React.ReactNode
Returns an appropriate icon component based on the file's MIME type or extension.
Internal State Management
The file upload uses a custom store implemented with:
createStorefunction creating a state container with listeners and dispatch.State includes:
files: Map ofFiletoFileState(progress, status, error).dragOver: Whether files are being dragged over dropzone.invalid: Whether the current file state is invalid (e.g., validation error).
Actions to add, remove files, update progress, set errors, clear all, and set drag state.
The store notifies subscribers on state changes using
React.useSyncExternalStore.
File Validation and Upload Workflow
File selection or drag-and-drop triggers
onFilesChange.Files are validated against:
Max number of files.
Custom validation function.
Accepted file types.
Max file size.
Accepted files are added to the store; rejected files trigger callbacks.
If an
onUploadfunction is provided, it is called with accepted files and callbacks for progress, success, and error.Progress updates dispatch store actions to update UI.
Errors and success update file status accordingly.
Users can delete individual files or clear all files.
Interaction with Other Parts of the System
Utility functions come from external imports:
cnfor className concatenation.Slotfrom@radix-ui/react-slotfor flexible component composition.Icon components from
lucide-reactprovide file type icons.
The file upload components can be used standalone or integrated into larger forms or UI frameworks.
Consumers provide upload logic via
onUploadcallback to integrate with backend APIs or storage services.Accessibility is ensured with ARIA roles, labels, and keyboard navigation support.
The
FileUploadRootcomponent provides context to children, enabling state sharing without prop drilling.
Important Implementation Details
Uses React contexts (
StoreContext,FileUploadContext,FileUploadItemContext) for state and metadata sharing.Uses a WeakMap cache to store and revoke object URLs for image previews to avoid memory leaks.
Supports controlled and uncontrolled modes for file state.
Throttles progress updates using
requestAnimationFramefor efficient UI updates.Validates files synchronously before upload and rejects invalid files with appropriate messages.
Drag-and-drop and paste support are implemented with proper event handling and accessibility considerations.
Uses
aria-*attributes extensively for screen reader support.Supports customizable rendering via
asChildprop and render props inFileUploadItemPreview.
Visual Diagram: Component Structure and Interaction
componentDiagram
direction LR
FileUploadRoot <|-- FileUploadDropzone
FileUploadRoot <|-- FileUploadTrigger
FileUploadRoot <|-- FileUploadList
FileUploadList <|-- FileUploadItem
FileUploadItem <|-- FileUploadItemPreview
FileUploadItem <|-- FileUploadItemMetadata
FileUploadItem <|-- FileUploadItemProgress
FileUploadItem <|-- FileUploadItemDelete
FileUploadRoot "provides" --> StoreContext
FileUploadRoot "provides" --> FileUploadContext
FileUploadDropzone "uses" --> StoreContext
FileUploadTrigger "uses" --> FileUploadContext
FileUploadList "uses" --> StoreContext,FileUploadContext
FileUploadItem "uses" --> StoreContext,FileUploadContext,FileUploadItemContext
FileUploadItemPreview "uses" --> FileUploadItemContext,FileUploadContext
FileUploadItemMetadata "uses" --> FileUploadItemContext,FileUploadContext
FileUploadItemProgress "uses" --> FileUploadItemContext
FileUploadItemDelete "uses" --> FileUploadItemContext,StoreContext
Summary
The file-upload.tsx file is a robust, accessible React file upload component suite that manages file selection, validation, uploading, and UI feedback via a modular set of components and hooks. It allows detailed customization and integration points for file handling workflows, making it suitable for complex file upload use cases in React applications.