Complete React file upload component library
Validation β’ Upload β’ Preview β’ Crop β’ Compress
Quick Start β’ What's New β’ Features β’ Docs β’ Examples
π Major update with powerful new capabilities!
|
Beautiful Material Design selection checkboxes with full dark mode support <FileMosaic
{...file}
selectable
selected
darkMode
/> |
Built-in edit icon for image files with callback support <FileMosaic
{...file}
onEdit={handleEdit}
preview
/> |
Extend functionality without bloating your bundle:
- @files-ui/crop (~11KB) β Interactive image cropping
- @files-ui/compress (~8KB) β Image optimization
npm install @files-ui/react
# or
yarn add @files-ui/reactimport { Dropzone, FileMosaic } from "@files-ui/react";
import { useState } from "react";
export default function App() {
const [files, setFiles] = useState([]);
return (
<Dropzone onChange={setFiles} value={files}>
{files.map((file) => (
<FileMosaic key={file.id} {...file} preview />
))}
</Dropzone>
);
}Live Demos:
| Demo | Link |
|---|---|
| Basic Sample π° | |
| Advanced Sample π¨ |
- β Drag & Drop β Intuitive file upload
- β File Validation β Type, size, custom validators
- β Upload Progress β Real-time upload tracking
- β Chunked Upload β Large file support
- β Server Actions β Next.js 16+ native support
- β Multiple Layouts β Grid (FileMosaic) & Card (FileCard)
- β Material Icons β Beautiful Material Design icons
- β Dark Mode β Built-in theme support
- β Selection API β Multi-select with checkboxes
- β Preview β Image and video fullscreen preview
- β Avatar β Profile image picker
- β TypeScript β Full type definitions
- β
Headless β
useFilesUI()hook for custom UIs - β Zero Dependencies β Core has no external deps
- β RSC Compatible β Next.js App Router ready
- β Framework Agnostic β Works with any React setup
| Component | Purpose | Key Props |
|---|---|---|
| <Dropzone/> | Main upload area | onChange, value, accept, maxFileSize |
| <FileMosaic/> | Grid file display | preview, onDelete, onEdit, selectable |
| <FileCard/> | Card file display | elevation, preview, darkMode |
| <Avatar/> | Profile image picker | src, alt, onChange |
| <FileInputButton/> | Button uploader | onChange, accept |
| <FullScreen/> | Modal preview | open, onClose |
πΈ Image Cropping & Compression
import { Dropzone, FileMosaic } from "@files-ui/react";
import { CropDialog, useCropDialog } from "@files-ui/crop/react";
import { compressImage } from "@files-ui/compress";
function ImageUploader() {
const [files, setFiles] = useState([]);
const { cropFile, openCrop, closeCrop, handleCropComplete } = useCropDialog({
onComplete: async (croppedFile) => {
// Compress after crop
const compressed = await compressImage(croppedFile, {
maxWidthOrHeight: 1920,
quality: 0.85
});
updateFile(compressed);
}
});
return (
<>
<Dropzone onChange={setFiles} value={files}>
{files.map(file => (
<FileMosaic key={file.id} {...file} onEdit={openCrop} />
))}
</Dropzone>
{cropFile && (
<CropDialog
file={cropFile}
onComplete={handleCropComplete}
onCancel={closeCrop}
/>
)}
</>
);
}π Dark Mode
import { Dropzone, FileMosaic, FilesUiProvider } from "@files-ui/react";
// Global dark mode
function App() {
return (
<FilesUiProvider config={{ darkMode: true }}>
<Dropzone onChange={setFiles} value={files}>
{files.map(file => (
<FileMosaic key={file.id} {...file} />
))}
</Dropzone>
</FilesUiProvider>
);
}
// Per-component dark mode
<FileMosaic {...file} darkMode />
<FileCard {...file} darkMode />βοΈ Multi-Select with Checkboxes
function FileSelector() {
const [files, setFiles] = useState([]);
const [selected, setSelected] = useState(new Set());
const handleSelect = (fileId, isSelected) => {
setSelected(prev => {
const next = new Set(prev);
isSelected ? next.add(fileId) : next.delete(fileId);
return next;
});
};
return (
<Dropzone onChange={setFiles} value={files}>
{files.map(file => (
<FileMosaic
key={file.id}
{...file}
selectable
selected={selected.has(file.id)}
onSelect={handleSelect}
/>
))}
</Dropzone>
);
}π Next.js Server Actions
// app/actions/upload.ts
"use server";
export async function uploadFile(formData: FormData) {
const file = formData.get("file") as File;
// Process file...
return { success: true, message: `Uploaded ${file.name}` };
}
// app/uploader.tsx
"use client";
import { Dropzone } from "@files-ui/react/client/dropzone";
import { uploadFile } from "./actions/upload";
export function Uploader() {
return <Dropzone action={uploadFile} />;
}π¨ Custom UI with Headless Hook
import { useFilesUI } from "@files-ui/react";
function CustomUploader() {
const { files, uploadFiles, getDragHandlers, getInputProps } = useFilesUI({
accept: "image/*",
maxFileSize: 5 * 1024 * 1024,
url: "/api/upload"
});
return (
<div {...getDragHandlers()} className="my-dropzone">
<input {...getInputProps()} />
<button onClick={() => uploadFiles()}>
Upload {files.length} files
</button>
</div>
);
}- π Complete Documentation β Component API, guides, examples
- π¨ Storybook β Interactive component playground
- π Changelog β Release history
- π TypeScript Definitions β Full type support
| Browser | Version |
|---|---|
| Chrome | β Latest |
| Firefox | β Latest |
| Safari | β 14+ |
| Edge | β Latest |
| Mobile | β iOS 14+, Android Chrome |
We welcome contributions! See CONTRIBUTING.md for guidelines.
Ways to contribute:
- π Report bugs via GitHub Issues
- β¨ Suggest features
- π Improve documentation
- π§ Submit pull requests
- π‘ GitHub Discussions β Ask questions
- π§ Email Support β Get help
- π Issue Tracker β Report bugs
If Files UI helped your project:
- β Star us on GitHub
- π¦ Share on social media
- π¬ Tell your developer friends
- π Write about it on your blog
It really helps us grow! π
MIT License β See LICENSE for details.
Β© 2024 Files UI Contributors
Made with β€οΈ by the Files UI Team
www.files-ui.com




