ns-ui

File Upload Seal

File upload where progress is the container: a loose dashed outline 24px outside the card pulls taut as the file uploads, clicks shut with a pucker at 100%, and relaxes back out with a wobble on failure.

Use when a file's own edges should read as the progress indicator — the gap between a dashed outline and the card IS the remaining upload, tightening to a taut seal at 100% with a physical end-state instead of a bar disappearing at full. Pick file-upload-thermal instead when the moment that matters is the drop itself (drag-in lift, accept/reject flight) rather than what happens to a file after it lands.

Install

npx shadcn add https://design.helpmarq.com/r/file-upload-seal.json
  • dropzone
  • file-upload
  • progress
  • form
  • svg
  • physics
  • accessibility

ns-ui / file-upload-seal — the gap between outline and card is the remaining upload

Asset delivery

Drop the final files for this deliverable. Each one seals shut as it finishes — no bar, just the file itself pulling taut.

Drop files to seal, or

Any file type · max 64.0 MB

  • brand-guidelines.pdf

    2.6 MB · sealed

  • hero-render-4k.mov

    uploading 39.3 MB… 58%

  • invoice-0042.pdf

    upload failed · 181 KB

3 files · 1 failed, re-drop to retry

Drop or browse a file: a loose dashed outline appears 24px outside the card and pulls tight as it uploads, clicking shut with a small pucker at 100%. A failed upload relaxes back out with a wobble instead of vanishing a bar.

Props

PropTypeDefaultDescription
defaultFiles?{ name: string; size: number; type: string; status?: UploadStatus; progress?: number; }[][]files rendered already in their final state — sealed, mid-upload, or failed — at mount, with no entrance animation
accept?string[][]allowed types: extensions (".png") or mime ("image/png", "image/*"); empty = any
maxSizeBytes?number25 * 1024 * 1024
uploadFile?(file: File, onProgress: (p: number) => void) => Promise<void>perform the real upload, reporting 0..1 via onProgress; reject to fail the seal. omit to use a built-in simulated upload.
onFilesChange?(files: VacuumSealFile[]) => void
className?string
aria-label?string"Upload files"