File upload

A complete file selection flow with constraints, progress, and error feedback.

Preview
File upload preview
Upload a coverSVG, PNG or JPG, up to 5 MB.Browse

When to use

  • Uploading files with per-file progress, retry, and remove.
  • Attachments that must be validated by type and size before upload.
  • Uploads that should abort when a file is removed, via the AbortSignal.

When not to use

  • Use file-dropzone when you only need to pick files without upload state.
  • Use import-mapper when uploaded data needs column mapping.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file-upload",
  "type": "registry:block",
  "title": "File upload",
  "description": "A complete file selection flow with constraints, progress, and error feedback.",
  "categories": [
    "blocks"
  ],
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/file-upload/file-upload.tsx",
      "type": "registry:component",
      "target": "@components/manicat/file-upload/file-upload.tsx"
    },
    {
      "path": "registry/components/file-upload/file-upload.module.css",
      "type": "registry:component",
      "target": "@components/manicat/file-upload/file-upload.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/blocks/file-upload",
  "meta": {
    "tier": "free",
    "kind": "block",
    "docs": "https://manicat.viraza.net/components/blocks/file-upload",
    "markdown": "https://uiarc.dev/components/blocks/file-upload/markdown"
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { FileUpload } from "@/components/manicat/file-upload/file-upload";

export function FileUploadExample() {
  return <FileUpload label="Upload a cover" description="SVG, PNG or JPG, up to 5 MB." maxSize={5 * 1024 * 1024} />;
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Cover upload A single-file uploader with a size limit and a hint about accepted formats.

Cover upload A single-file uploader with a size limit and a hint about accepted formats. preview
Upload a coverSVG, PNG or JPG, up to 5 MB.Browse

Keyboard interactions

KeysAction
Enter / SpaceOpens the file picker from the dropzone.
TabMoves through the dropzone and each row's retry and remove buttons.

Accessibility

  • The dropzone is role=button with aria-disabled and aria-describedby for the helper text.
  • An aria-live polite status line announces added, rejected, uploaded, failed, and removed files.
  • Retry and remove buttons are labelled with the file name.
  • After removing a file, focus moves to the neighbouring remove button or back to the dropzone.

Motion

  • The dropzone label swaps to Drop to add files while dragging; rows open and close their height on a spring.
  • One spring drives the progress bar and the counted percentage, the bar folds away once the file lands, and a check draws in.
  • Reduced motion jumps progress and replaces height and blur swaps with fades.

Responsive behavior

  • The dropzone and list fill their container; file names ellipsize on one line.
  • On touch devices the dropzone opens the native file picker, since drag and drop is desktop only.

Performance

  • Uploads run through your onUpload, one call per valid file; there is no concurrency limit, so queue large batches yourself.
  • The file list is not virtualized; keep it to a reasonable number of files.

Notes for AI

  • Use when files are uploaded with progress. Use file-dropzone when you only need to pick files without upload state.
  • Wire onUpload to fetch or XHR and honour the AbortSignal; without onUpload files are only listed and validated.