Upload

Pick or drop files with a compact upload button or a dashed dropzone, built on react-dropzone.

Upload is a client component built on react-dropzone. It renders either a compact button (type="button") or a dashed drop area (type="dropzone"). Both open the file picker on click and accept dropped files. It accepts every useDropzone option, so validation (accept, maxSize, multiple, and so on) is handled by react-dropzone.

Accepted: none

Import#

typescript
import { Upload } from "@reactberry/system/blocks";
import type { UploadProps, UploadComponent, DropzoneProps } from "@reactberry/system/blocks";

Usage#

typescript
import { useState } from "react";
import { Upload } from "@reactberry/system/blocks";

export default function AvatarUpload() {
  const [loading, setLoading] = useState(false);

  return (
    <Upload
      type="dropzone"
      accept={{ "image/*": [] }}
      maxSize={5 * 1024 * 1024}
      multiple={false}
      loading={loading}
      dropzoneProps={{ title: "Upload an avatar", description: "Images up to 5 MB" }}
      onDropAccepted={async ([file]) => {
        setLoading(true);
        await uploadAvatar(file);
        setLoading(false);
      }}
      onDropRejected={(rejections) => console.warn(rejections[0]?.errors)}
    />
  );
}

Examples#

Custom button#

Pass any icon component to icon and restyle the button through buttonProps. Here accept limits the picker to PDFs.

Dropzone with an icon#

Use dropzoneProps to give the empty state an icon, a title and a description that explain what to drop.

Loading#

Set loading while files are being processed. Both looks show progress and stop accepting files.

API#

Upload#

PropTypeDefaultDescription
type
"button""dropzone"
"button"

Which look to render: a compact button or a dashed drop area.

label
ReactNode
—

Button text. In the dropzone, the fallback text when dropzoneProps.description is not set.

icon
any
—

Button only. A component (not an element) rendered at 1rem. Defaults to IconUpload.

dropzoneProps
Partial<DropzoneProps>
—

Dropzone only. Content of the empty state: icon, title, description, showPreview and imgSrc.

loading
boolean
—

Disables the input and shows a loading state ("Working" on the button, "Processing…" in the dropzone).

buttonProps
any
—

Button only. Props spread onto the Button (defaults are variant="primary" and $size="small").

dropzoneContainerProps
any
—

Dropzone only. Props spread onto the drop area Text.

Upload also accepts every useDropzone option: accept, onDrop, onDropAccepted, onDropRejected, maxSize, minSize, maxFiles, multiple, disabled, noClick, validator, and so on.

DropzoneProps#

PropTypeDefaultDescription
icon
ReactNode
—

Element shown above the title.

title
ReactNode
—

Heading of the empty state.

description
ReactNode
—

Text under the title. Falls back to label, then to "Drag and drop files here, or click to select files".

showPreview
boolean
—

With imgSrc, renders the image with next/image instead of the empty state and removes the padding.

imgSrc
string
—

Image shown when showPreview is set.

Accessibility#

  • The button look renders a native <button type="button"> with role="button", and is disabled while loading.
  • The dropzone look is a focusable element (tabindex="0") handled by react-dropzone.
KeyAction
Tab

Moves focus to the button or dropzone.

EnterorSpace

Opens the file picker.

Notes#

  • The dropzone colour follows the drag state: success.subtle when the dragged files are accepted, error.subtle when rejected, info.subtle while dragging, and brand.subtle otherwise.
  • loading is passed to react-dropzone as disabled; an explicit disabled prop takes precedence.
  • The button spreads react-dropzone's root props after buttonProps, so root handlers such as onClick and onKeyDown win over the same keys in buttonProps.
  • The preview uses next/image without width, height, or fill. Next.js requires those for string sources, so a plain URL (including an object URL from URL.createObjectURL) in imgSrc makes next/image throw. Use your own preview next to the dropzone until this is changed.
  • dropzoneContainerProps.style replaces the dropzone's default inline style (padding, opacity, transition) rather than merging with it.
  • UploadContainerProps (the internal props for the two looks) is not exported.