Upload Button
A button that opens the file dialog and also accepts files dropped onto it.
It takes the same props as the shadcn/ui Button. Several buttons can feed the same UploaderProvider, so you can pair them with a FileList or build your own list with useUploader.
Installation
Use the shadcn CLI to add the component to your project.
npx shadcn@latest add https://edgestore.dev/r/upload-button.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 { dropzoneState, useUploadDropzone } from './dropzone';
/**
* Props for the UploadButton component.
*/
export type UploadButtonProps = Omit<
React.ComponentProps<typeof Button>,
'onClick' | 'type' | 'className'
> & {
className?: string;
/**
* Accepted file types.
*/
accept?: Accept;
/**
* Human-readable list of accepted types, used in messages.
*/
typesLabel?: string;
/**
* Maximum file size in bytes.
*/
maxSize?: number;
/**
* Maximum number of files allowed in total.
*/
maxFiles?: number;
/**
* Whether more than one file can be picked at once.
* @default true
*/
multiple?: boolean;
/**
* Called with messages about files that were not added
* (wrong type, too large, over the limit).
*/
onRejected?: (messages: string[]) => void;
};
/**
* A button that opens the file dialog and also accepts files dropped onto it.
* Several buttons can feed the same `UploaderProvider`.
*
* @example
* ```tsx
* <UploadButton maxSize={1024 * 1024 * 10} onRejected={setErrors}>
* <UploadIcon /> Upload files
* </UploadButton>
* ```
*/
export function UploadButton({
accept,
typesLabel,
maxSize,
maxFiles,
multiple = true,
onRejected,
disabled,
className,
children,
...props
}: UploadButtonProps) {
const { getRootProps, getInputProps, isDragActive, isDragReject, isFull } =
useUploadDropzone({
accept,
typesLabel,
maxSize,
maxFiles,
multiple,
disabled,
onRejected,
// A <button> already opens the dialog on Enter and Space.
noKeyboard: true,
});
// react-dropzone keeps its hidden input in the layout, so it is wrapped with the
// button to keep it out of the parent's gaps.
return (
<span className="inline-flex">
<Button
{...props}
{...getRootProps({ role: 'button' })}
{...dropzoneState({ isDragActive, isDragReject })}
type="button"
disabled={disabled || isFull}
className={cn(
'data-[dragging]:ring-[3px] data-[dragging]:ring-ring/50 data-[rejected]:ring-destructive/40',
className,
)}
>
{children}
</Button>
<input {...getInputProps()} />
</span>
);
}Usage
Install or copy the component from Installation before using this example.
'use client';
import { UploadErrors } from '@/components/upload/dropzone';
import { FileList } from '@/components/upload/multi-file';
import { UploadButton } from '@/components/upload/upload-button';
import {
UploaderProvider,
type UploadFn,
} from '@/components/upload/uploader-provider';
import { useEdgeStore } from '@/lib/edgestore';
import { UploadIcon } from 'lucide-react';
import * as React from 'react';
export function UploadButtonUsage() {
const [errors, setErrors] = React.useState<string[]>([]);
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>
<UploadButton
maxSize={1024 * 1024 * 1} // 1 MB
onRejected={setErrors}
>
<UploadIcon /> Upload files
</UploadButton>
<UploadErrors errors={errors} onDismiss={() => setErrors([])} />
<FileList />
</UploaderProvider>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
maxFiles | number | Maximum number of files in total. | |
multiple | boolean | true | Whether more than one file can be picked at once. |
onRejected | (messages: string[]) => void | Called with messages about files that were not added. | |
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. |
variant, size, ... | Any other Button prop. |