Upload Dialog
The "Add files" modal — drop or browse, review the staged files, set each file's details, then Add. It stages files; the host uploads them.
- Status
- Since
0.23.62- Accessibility pattern
- named dialog, drop surface and remove buttons
Last updated
Install
Add Upload Dialog from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/upload-dialogThe same command installs the registry items it composes: @vegastack/attachment, @vegastack/button, @vegastack/dialog, @vegastack/dropzone, @vegastack/empty, @vegastack/field, @vegastack/image, @vegastack/use-file-drop.
Usage
import {
UploadDialog,
UploadDialogFileRow,
} from "@/components/ui/upload-dialog";
<Button variant="outline" onClick={() => setOpen(true)}>
<Plus />
Add files
</Button>
<UploadDialog
open={open}
onOpenChange={setOpen}
dropHint="or click to browse — images up to 20 MB, other files up to 500 MB"
validate={(file) => (tooLarge(file) ? `${file.name} is too large.` : null)}
renderDetails={(files) =>
files.map((f) => (
<UploadDialogFileRow key={f.id} file={f}>
{/* the host's fields for this file, keyed by f.id */}
</UploadDialogFileRow>
))
}
detailsValid={(files) => files.every(hasACategory)}
onSubmit={(files) => startUploads(files)}
/>;Scope
| Behaviour | Where it lives |
|---|---|
| Drop, browse, paste, type and size refusal | UploadDialog over Dropzone |
| The staged list and the details view | UploadDialog; the details' fields and their state are the host's |
| Uploading, progress, retry | Host — Attachment tiles on page |
Anatomy
Examples
Files, then details
The first view is a drop zone with the staged files under it — a thumbnail for an image or a file
icon, the name, the size and a × to remove it. Next is enabled once a file is staged. The details
view shows one UploadDialogFileRow per file, with the host's fields beside the thumbnail; Back
returns to the files, and Add hands the staged files to onSubmit and closes the dialog, so the
host's upload queue carries on on the page.
Without details
Leave out renderDetails and the dialog has one view: its primary action adds straight from the
staged list. maxFiles caps how many files can be staged, and says so when a drop goes over.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
onOpenChange* | (open: boolean) => void | — | Called when the dialog asks to open or close. |
onSubmit* | (files: UploadDialogFile[]) => void | — | Receives the staged files; the dialog then closes. |
open* | boolean | — | Whether the dialog is open (controlled). |
accept | Accept | — | Accepted types, MIME-to-extensions (react-dropzone's Accept shape):
{ "image/*": [".png", ".jpg"] }. |
description | React.ReactNode | — | A line under the title. |
detailsValid | ((files: UploadDialogFile[]) => boolean) | — | Whether the details are complete for these files; false disables Add. |
dropHint | React.ReactNode | "or click to browse" | The drop area's hint — the accepted types and limits. |
dropTitle | React.ReactNode | "Drop files here" | The drop area's title. |
maxFiles | number | — | The most files that can be staged; extra files are refused with a message. |
maxSize | number | — | Per-file maximum size in bytes. |
multiple | boolean | true | Allow more than one file per batch. |
renderDetails | ((files: UploadDialogFile[], helpers: { remove: (id: string) => void; }) => React.ReactNode) | — | The details view: one row per staged file, with the host's fields. Without it the dialog has one view and its primary action submits. |
submitLabel | string | "Add" | The submit button's label. |
title | string | "Add files" | The dialog title. |
validate | ((file: File) => string | null) | — | Refuse a file with a message (return null to accept it) — per-type size limits, formats the host can't take. |
Data attributes and CSS variables on UploadDialog
| Attribute | Values |
|---|---|
data-slot | "upload-dialog" | "upload-dialog-details" | "upload-dialog-files" |
data-stage | mirrors a prop or state value |
UploadDialogFileRow
| Prop | Type | Default | Description |
|---|---|---|---|
file* | UploadDialogFile | — | The staged file the row describes. |
Accessibility
- The dialog is named by its title; focus moves into it on open and back to the trigger on close.
- The drop surface is one named control (
Drop files here or browse) that opens the file picker. - Refused files are listed with a reason under the drop zone.
- Each staged file's × is named
Remove {file name}.
| Key | Action |
|---|---|
| Enter / Space | On the drop surface: browse files. |
| Tab | Move through the files and footer. |
| Esc | Close the dialog. |
| Contract | States tested |
|---|---|
| Behaviour | empty, staged, refused, details, submit |
| Accessibility | labeled, semantic-html, browser-accessibility-test |
| Visual | default, staged, details |