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'sFileTriggerandDropzonecomponents
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.jsonpnpm dlx shadcn@latest add https://oui.open.gov.sg/r/file-dropzone.jsonnpx shadcn@latest add https://oui.open.gov.sg/r/file-dropzone.jsonbunx --bun shadcn@latest add https://oui.open.gov.sg/r/file-dropzone.jsonThe 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
maxFileSizeandminFileSize(in bytes), with optional per-type limits viamaxFileSizeByType - Number of files: Limited by
maxFiles
When validation fails, the component will:
- Trigger the
onErrorcallback with an error message (if provided) - Mark rejected files with error information
- Display rejected files if
showRejectedFilesis 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
| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | - | The label for the file dropzone |
description | React.ReactNode | - | The description text shown below the dropzone |
errorMessage | React.ReactNode | - | The error message to display when validation fails |
value | FileItem[] | - | The currently selected files (controlled) |
defaultValue | FileItem[] | [] | 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 |
allowedMimeTypes | string[] | [] (all types allowed) | Array of allowed MIME types (supports wildcards like image/*) |
maxFileSize | number | Number.POSITIVE_INFINITY | Maximum file size in bytes |
maxFileSizeByType | MaxFileSizeRule[] | - | Per-MIME-type max file size rules (first match wins, falls back to maxFileSize) |
fileSizeText | string | - | Custom file size text override (replaces auto-generated text) |
minFileSize | number | 0 | Minimum file size in bytes |
maxFiles | number | 1 | Maximum number of files allowed |
showFileSizeText | boolean | true | Whether to show file size information below the dropzone |
showRejectedFiles | boolean | false | Whether to display rejected files in the component |
hideDropzoneOnValue | boolean | true if maxFiles === 1 | Whether 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 |
isDisabled | boolean | false | Whether the component is disabled |
isReadOnly | boolean | false | Whether the component is read-only |
isInvalid | boolean | false | Whether the component should display as invalid |
isRequired | boolean | false | Whether the component is required |
classNames | SlotsToClasses<FileDropzoneSlots> | - | Custom CSS classes for component slots |
itemClassNames | SlotsToClasses<FileInfoDropzoneSlots> | - | Custom CSS classes for file info item slots |
validator | DropzoneOptions["validator"] | - | Custom file validator function |
children | (props: FileItemsRenderProps) => ReactNode | - | Render prop for custom file item rendering |
Field
Primitives for composing accessible form fields with labels, descriptions, errors, and input grouping. Most callers should use a higher-level component (TextField, etc.) instead.
GovtBannerNew
The official Singapore Government masthead, pinned at the top of the page with an expandable "How to identify" panel. For system messages use Banner.