Open UI

FileDropzone

A component that allows users to upload files via drag-and-drop or file selection.

If you want a more react-aria-componentish component, check out rac's FileTrigger and Dropzone components

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return <FileDropzone aria-label="Upload files" />}

Usage

import { FileDropzone } from "@opengovsg/oui"
<FileDropzone aria-label="Upload files" />

Alternatively, install the component as local source via the shadcn CLI:

npx shadcn@latest add https://oui.open.gov.sg/r/file-dropzone.json
pnpm dlx shadcn@latest add https://oui.open.gov.sg/r/file-dropzone.json
npx shadcn@latest add https://oui.open.gov.sg/r/file-dropzone.json
bunx --bun shadcn@latest add https://oui.open.gov.sg/r/file-dropzone.json

The FileDropzone component provides an accessible way for users to upload files through drag-and-drop or file selection. It supports various file types, size restrictions, and multiple file uploads.

If the component does not have a visible label (by passing a label prop), an aria-label or aria-labelledby prop must be passed instead to identify it to assistive technology.

Examples

With Label and Description

Provide clear instructions to users with labels and descriptions.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Upload your documents"      description="All file types are accepted"    />  )}

Allowed MIME Types

Use the allowedMimeTypes prop to restrict which file types can be uploaded. Wildcards are supported (e.g., image/*).

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Upload images"      description="Only image files are allowed"      allowedMimeTypes={["image/*"]}      aria-label="Upload images"    />  )}

Multiple Files

Use the maxFiles prop to allow users to upload multiple files at once.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Upload multiple files"      description="You can upload up to 5 files"      maxFiles={5}      hideDropzoneOnValue={false}    />  )}

File Size Limits

Set maxFileSize and minFileSize to enforce file size restrictions. Sizes are specified in bytes.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Upload documents"      maxFileSize={2 * 1000 * 1000} // 2MB      minFileSize={10 * 1000} // 10KB    />  )}

Per-MIME-Type File Size Limits

Use maxFileSizeByType to set different maximum file sizes for different MIME types. Rules are matched in order — first match wins. Unmatched types fall back to maxFileSize.

You can also pass a custom fileSizeText string to override the auto-generated file size text.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Upload documents"      allowedMimeTypes={[        "application/zip",        "application/x-zip-compressed",        "image/*",        "text/*",      ]}      maxFileSize={4.5 * 1000 * 1000} // 4.5MB default      maxFileSizeByType={[        {          mimeTypes: ["application/zip", "application/x-zip-compressed"],          maxFileSize: 200 * 1000, // 200KB          label: ".zip files",        },      ]}      fileSizeBase="decimal"    />  )}

Error Handling

The FileDropzone automatically validates files against your constraints. Use the onError callback to handle validation errors and display error messages.

import { useState } from "react"import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  const [error, setError] = useState<string>("")  return (    <FileDropzone      label="Upload PDF only"      allowedMimeTypes={["application/pdf"]}      maxFileSize={1000 * 1000} // 1MB      onChange={() => setError("")}      onError={setError}      isInvalid={!!error}      errorMessage={error}    />  )}

Image Preview

By default, image files are shown with a small preview. Use the imagePreview prop to control the preview size or disable it entirely.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex w-full flex-col gap-6">      <FileDropzone        label="Small preview (default)"        allowedMimeTypes={["image/*"]}        imagePreview="small"      />      <FileDropzone        label="Large preview"        allowedMimeTypes={["image/*"]}        imagePreview="large"      />      <FileDropzone        label="No preview"        allowedMimeTypes={["image/*"]}        imagePreview={null}      />    </div>  )}

Show Rejected Files

Use showRejectedFiles to display files that failed validation along with their error messages.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Upload text files only"      description="Try uploading an image to see rejection"      allowedMimeTypes={["text/plain"]}      showRejectedFiles      hideDropzoneOnValue={false}    />  )}

Controlled

Use the value and onChange props to control the selected files programmatically.

import { useState } from "react"import type { FileItem } from "@opengovsg/oui"import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  const [files, setFiles] = useState<FileItem[]>([])  return (    <div className="w-full">      <FileDropzone        value={files}        onChange={setFiles}        label="Controlled FileDropzone"        maxFiles={3}      />      <div className="mt-4">        <strong>Selected Files:</strong>        {files.length === 0 ? (          <p>No files selected.</p>        ) : (          <ul className="list-inside list-disc">            {files.map((file) => (              <li key={file.name}>                {file.name} - {(file.size / 1024).toFixed(2)} KB              </li>            ))}          </ul>        )}      </div>    </div>  )}

Custom File Rendering

Use the children render prop to customize how uploaded files are displayed.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Custom file display"      allowedMimeTypes={["image/*"]}      maxFiles={3}    >      {({ file, removeFile }) => {        const objectUrl = URL.createObjectURL(file)        return (          <div key={file.name} className="mt-4 rounded-lg border p-4">            <div className="flex items-center gap-4">              <img                src={objectUrl}                alt={file.name}                className="h-20 w-20 rounded object-cover"                onLoad={() => URL.revokeObjectURL(objectUrl)}              />              <div className="flex-1">                <p className="font-medium" title={file.name}>                  {file.name}                </p>                <p className="text-sm text-gray-600">                  {(file.size / 1024).toFixed(2)} KB                </p>              </div>              <button                type="button"                onClick={removeFile}                className="text-sm text-red-600 underline hover:text-red-700"              >                Remove              </button>            </div>          </div>        )      }}    </FileDropzone>  )}

Sizes

Use the size prop to change the size of the file dropzone.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex w-full flex-col gap-6">      <FileDropzone        label="Small"        description="This is a small file dropzone"        size="sm"      />      <FileDropzone        label="Medium (default)"        description="This is a medium file dropzone"        size="md"      />    </div>  )}

Disabled

Use the isDisabled prop to disable the file dropzone.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Disabled file dropzone"      description="This dropzone cannot be interacted with"      isDisabled    />  )}

Read Only

Use the isReadOnly prop to make the file dropzone read-only. Users can view uploaded files but cannot add or remove them.

import { FileDropzone } from "@opengovsg/oui"export const Example = () => {  return (    <FileDropzone      label="Read-only file dropzone"      description="Files can be viewed but not modified"      isReadOnly    />  )}

Validation

The FileDropzone component automatically validates files based on:

  • MIME type: Specified via allowedMimeTypes
  • File size: Controlled by maxFileSize and minFileSize (in bytes), with optional per-type limits via maxFileSizeByType
  • Number of files: Limited by maxFiles

When validation fails, the component will:

  1. Trigger the onError callback with an error message (if provided)
  2. Mark rejected files with error information
  3. Display rejected files if showRejectedFiles is enabled

Accessibility

  • The component uses proper ARIA attributes for screen reader support
  • Keyboard navigation is fully supported (Enter/Space to open file selector)
  • File validation errors are communicated to assistive technology
  • Error messages are properly associated with the component

Slots

Dropzone (classNames)

  • base: The root container for the dropzone
  • group: The wrapper for the dropzone area
  • dropzone: The interactive drop target area
  • icon: The upload icon displayed in the dropzone
  • text: The instructional text in the dropzone
  • dropzoneHighlight: The highlighted "Choose files" text
  • description: The description text below the dropzone
  • errorMessage: The error message text

FileItem (itemClassNames)

  • base: The root container for the file item component
  • container: The container for the file item content (text and actionButton)
  • textContainer: The container for the file name and size text
  • imageContainer: The container for the file preview image
  • image: The file preview image
  • name: The file name text
  • size: The file size text
  • error: The file rejection error
  • actionButton: The action button for the file item

Custom Styles

You can customize the FileDropzone component by passing custom Tailwind CSS classes to the component slots via the classNames and itemClassNames prop.

<FileDropzone
  aria-label="Upload files"
  classNames={{
    dropzone: "border-dashed border-2 border-blue-500",
    icon: "text-blue-500",
    text: "text-sm",
  }}
  itemClassNames={{
    base: "flex items-center space-x-2",
    textContainer: "flex-1",
    imageContainer: "w-12 h-12",
    image: "object-cover",
    name: "font-medium",
    size: "text-sm text-gray-500",
    error: "text-red-500",
    actionButton: "text-blue-500",
  }}
/>

Props

FileDropzone

PropTypeDefaultDescription
labelReact.ReactNode-The label for the file dropzone
descriptionReact.ReactNode-The description text shown below the dropzone
errorMessageReact.ReactNode-The error message to display when validation fails
valueFileItem[]-The currently selected files (controlled)
defaultValueFileItem[][]The default files (uncontrolled)
onChange(files: FileItem[]) => void-Callback fired when the selected files change
onError(errorMessage: string) => void-Callback fired when file validation fails
onRejection(rejections: FileItem[]) => void-Callback fired when files are rejected
allowedMimeTypesstring[][] (all types allowed)Array of allowed MIME types (supports wildcards like image/*)
maxFileSizenumberNumber.POSITIVE_INFINITYMaximum file size in bytes
maxFileSizeByTypeMaxFileSizeRule[]-Per-MIME-type max file size rules (first match wins, falls back to maxFileSize)
fileSizeTextstring-Custom file size text override (replaces auto-generated text)
minFileSizenumber0Minimum file size in bytes
maxFilesnumber1Maximum number of files allowed
showFileSizeTextbooleantrueWhether to show file size information below the dropzone
showRejectedFilesbooleanfalseWhether to display rejected files in the component
hideDropzoneOnValuebooleantrue if maxFiles === 1Whether to hide the dropzone when files are uploaded
imagePreview"small" | "large" | null"small"Size of image preview, or null to disable previews
size"sm" | "md""md"The size of the component
isDisabledbooleanfalseWhether the component is disabled
isReadOnlybooleanfalseWhether the component is read-only
isInvalidbooleanfalseWhether the component should display as invalid
isRequiredbooleanfalseWhether the component is required
classNamesSlotsToClasses<FileDropzoneSlots>-Custom CSS classes for component slots
itemClassNamesSlotsToClasses<FileInfoDropzoneSlots>-Custom CSS classes for file info item slots
validatorDropzoneOptions["validator"]-Custom file validator function
children(props: FileItemsRenderProps) => ReactNode-Render prop for custom file item rendering

On this page