Skip to content
Component installs need the registry setup
VegaStack Design

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
stable
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-dialog

The 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

BehaviourWhere it lives
Drop, browse, paste, type and size refusalUploadDialog over Dropzone
The staged list and the details viewUploadDialog; the details' fields and their state are the host's
Uploading, progress, retryHost — Attachment tiles on page

Anatomy

UploadDialog — data-slot="upload-dialog" | "upload-dialog-details" | "upload-dialog-files"
UploadDialogFileRow

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

PropTypeDefaultDescription
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).
acceptAccept—Accepted types, MIME-to-extensions (react-dropzone's Accept shape): { "image/*": [".png", ".jpg"] }.
descriptionReact.ReactNode—A line under the title.
detailsValid((files: UploadDialogFile[]) => boolean)—Whether the details are complete for these files; false disables Add.
dropHintReact.ReactNode"or click to browse"The drop area's hint — the accepted types and limits.
dropTitleReact.ReactNode"Drop files here"The drop area's title.
maxFilesnumber—The most files that can be staged; extra files are refused with a message.
maxSizenumber—Per-file maximum size in bytes.
multiplebooleantrueAllow 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.
submitLabelstring"Add"The submit button's label.
titlestring"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

AttributeValues
data-slot"upload-dialog" | "upload-dialog-details" | "upload-dialog-files"
data-stagemirrors a prop or state value

UploadDialogFileRow

PropTypeDefaultDescription
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}.
KeyAction
Enter / SpaceOn the drop surface: browse files.
TabMove through the files and footer.
EscClose the dialog.
ContractStates tested
Behaviourempty, staged, refused, details, submit
Accessibilitylabeled, semantic-html, browser-accessibility-test
Visualdefault, staged, details

Do / Don't

Do
Open it from an Add files button, ask for each file's details in the second view, and show upload progress on the page's own tiles after Add.
Don't
Upload inside the dialog or keep it open while files upload — the dialog stages files; the page owns the upload queue.

On this page