Multi-file
A component for uploading multiple files with a progress list, retry and a running total.
Click to upload or drag and drop
up to 1 MB each · 10 max
Each file shows its type, size and progress. Failed uploads show the error with a retry button, and running uploads can be canceled.
Installation
Use the shadcn CLI to add the component to your project.
npx shadcn@latest add https://edgestore.dev/r/file-uploader.jsonSetup for manual installation
First you will need to follow the manual install setup guide.
Copy this component
'use client';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import * as React from 'react';
import { type Accept } from 'react-dropzone';
import { Dropzone } from './dropzone';
import { FileList } from './file-list';
import { formatFileSize } from './upload-utils';
import { useUploader } from './uploader-provider';
/**
* Props for the FileUploader component.
*/
export type FileUploaderProps = React.ComponentProps<'div'> & {
/**
* Maximum number of files allowed in total.
*/
maxFiles?: number;
/**
* Maximum file size in bytes.
*/
maxSize?: number;
/**
* Accepted file types.
*
* @example
* ```tsx
* accept={{ 'application/pdf': ['.pdf'] }}
* ```
*/
accept?: Accept;
/**
* Human-readable list of accepted types, shown in the hint and messages.
*
* @example "PDF or TXT"
*/
typesLabel?: string;
/**
* Whether the uploader is disabled.
*/
disabled?: boolean;
/**
* Additional className for the dropzone.
*/
dropzoneClassName?: string;
/**
* Additional className for the file list.
*/
fileListClassName?: string;
};
/**
* A dropzone with a list of files, their progress and a summary.
*
* @example
* ```tsx
* <FileUploader
* maxFiles={5}
* maxSize={1024 * 1024 * 10} // 10MB
* accept={{ 'application/pdf': ['.pdf'] }}
* typesLabel="PDF"
* />
* ```
*/
export function FileUploader({
maxFiles,
maxSize,
accept,
typesLabel,
disabled,
className,
dropzoneClassName,
fileListClassName,
...props
}: FileUploaderProps) {
const { fileStates, resetFiles } = useUploader();
const completed = fileStates.filter((fs) => fs.status === 'COMPLETE').length;
const totalSize = fileStates.reduce((sum, fs) => sum + fs.file.size, 0);
return (
<div className={cn('flex w-full flex-col gap-3', className)} {...props}>
<Dropzone
dropzoneOptions={{ maxFiles, maxSize, accept, typesLabel }}
disabled={disabled}
className={dropzoneClassName}
/>
<FileList className={fileListClassName} disabled={disabled} />
{fileStates.length > 0 && (
<div className="flex items-center justify-between gap-3 text-xs text-muted-foreground">
<span className="tabular-nums">
{completed} of {fileStates.length} uploaded ·{' '}
{formatFileSize(totalSize)}
</span>
<Button
type="button"
variant="ghost"
size="sm"
onClick={resetFiles}
disabled={disabled}
>
Clear all
</Button>
</div>
)}
</div>
);
}Usage
Install or copy the component from Installation before using this example.
'use client';
import { FileUploader } from '@/components/upload/multi-file';
import {
UploaderProvider,
type UploadFn,
} from '@/components/upload/uploader-provider';
import { useEdgeStore } from '@/lib/edgestore';
import * as React from 'react';
export function MultiFileDropzoneUsage() {
const { edgestore } = useEdgeStore();
const uploadFn: UploadFn = React.useCallback(
async ({ file, onProgressChange, signal }) => {
const res = await edgestore.publicFiles.upload({
file,
signal,
onProgressChange,
});
// you can run some server action or api here
// to add the necessary data to your database
console.log(res);
return res;
},
[edgestore],
);
return (
<UploaderProvider uploadFn={uploadFn} autoUpload>
<FileUploader
maxFiles={5}
maxSize={1024 * 1024 * 1} // 1 MB
accept={{
'application/pdf': ['.pdf'],
'text/plain': ['.txt'],
}}
typesLabel="PDF or TXT"
/>
</UploaderProvider>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
maxFiles | number | Maximum number of files in total. | |
maxSize | number | Maximum file size in bytes. | |
accept | Accept | all types | Accepted file types, in react-dropzone's format. |
typesLabel | string | Human-readable list of accepted types, shown in the hint and in error messages. | |
disabled | boolean | false | Disables the component. |
dropzoneClassName | string | Additional className for the dropzone. | |
fileListClassName | string | Additional className for the file list. |
Building your own layout
FileUploader is a Dropzone and a FileList, so you can arrange the two differently.