Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Inputs

File Upload

Inputs

File Upload React component for Tailwind CSS v4

Emoji PickerFilter Builder
Live props
Appearance
variant
size
radius
tone
color
Behavior
accept
maxSize
maxFilesNo limit
multiple
autoUpload
maxConcurrent3
sortable
camera
paste
locale
Field
label
description
error
required
invalid
disabled
name
Code
import { FileUpload } from "@/components/ui/file-upload"

<FileUpload />
  1. Home
  2. Components
  3. Inputs
  4. File Upload
Pro

File Upload React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React file upload with drag and drop, paste and camera capture: validation, previews, per-file progress, and list, grid or avatar layouts.

Drag files from your desktop over the page and every dropzone lights up with "Drop to upload"; over the zone itself it tints, grows a touch and its icon lifts. Click the dropzone (or focus it and press Enter or Space) to open the file picker, or paste a copied file or screenshot with Ctrl+V / Cmd+V while focus is inside. Each file is checked against the accepted types, the size limit and the file limit: rejected files are listed under the dropzone with the reason and never silently dropped. Accepted images show a preview right away. With an upload function each row fills a progress bar (a ring on photos and the avatar), at most three at a time by default, and pops a green check when it finishes; X cancels a running upload, the circular arrow retries a failed or canceled one, the trash can removes a file and aborts its upload. In the photo grid the first photo is the cover: drag a tile to reorder, or focus it and press Space, move it with the arrow keys and press Space again (Escape cancels); screen readers hear every move. On a phone the dropzone reads "Tap to choose files", remove buttons on photos are always visible, a 250ms press starts reordering so the page still scrolls, every action has a 44px target, and with camera on a "Take photo" button opens the camera.

File Upload is a Pro React component in the React input components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add file-upload, together with Field, Button, Badge, Number format, useLocale and 5 more and the npm packages @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add file-upload

File Upload is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import FileUpload from @/components/ui/file-upload.

import { FileUpload, type UploadFile } from "@/components/ui/file-upload"

// your endpoint; signal aborts, onProgress fills the bar
function uploadFile(file: File, { signal, onProgress }: { signal: AbortSignal; onProgress: (fraction: number) => void }) {
  return new Promise<{ url: string }>((resolve, reject) => {
    const request = new XMLHttpRequest()
    request.open("POST", "/api/upload")
    request.upload.onprogress = (event) => onProgress(event.loaded / event.total)
    request.onload = () => (request.status < 300 ? resolve(JSON.parse(request.responseText)) : reject(new Error("Upload failed")))
    request.onerror = () => reject(new Error("No connection"))
    signal.addEventListener("abort", () => request.abort())
    const body = new FormData()
    body.append("file", file)
    request.send(body)
  })
}

const existing: UploadFile[] = [
  { id: "contract", name: "purchase-agreement.pdf", size: 1887437, type: "application/pdf", url: "/files/purchase-agreement.pdf", status: "done", progress: 1 },
]

export function PropertyDocuments() {
  return (
    <FileUpload
      label="Property documents"
      description="PDFs or images, up to 10 MB each"
      accept="image/*,.pdf"
      defaultValue={existing}
      upload={uploadFile}
      locale="en-US"
      labels={{ title: "Drop your documents here or", browse: "browse" }}
    />
  )
}

Props

  • variantdefault "list"
    "list" | "grid" | "avatar" | "compact"

    list: a dropzone above a list of rows (documents). grid: photo tiles you can reorder, the first is the cover (listing photos). avatar: one round photo with a camera badge. compact: an attach button and small rows (chat, comments).

  • acceptdefault ""
    string

    Accepted types, like the native attribute: "image/*,.pdf". Extensions match case-insensitively, wildcards match the MIME type. Also feeds the picker and the automatic hint. The exported acceptsFile(file, accept) runs the same check.

  • maxSizedefault "10485760"
    number

    Largest file in bytes (10 MB); 0 means no limit. Bigger files are rejected with labels.tooLarge.

  • maxFilesdefault 0
    number

    How many files the list may hold; 0 means no limit. Extra files are rejected, not dropped. At the limit the dropzone turns off and shows "8 / 8". The avatar always holds one.

  • multipledefault true
    boolean

    Pick several files at once. Off, a new file replaces the current one.

  • upload
    (file: File, context: { signal: AbortSignal; onProgress: (fraction: number) => void }) => Promise<unknown>

    Uploads one file. Pass signal to fetch so cancel, remove and unmount abort it, call onProgress with 0..1 (throttled to one update per 100ms per file). What it resolves with lands in the file's response; a rejection keeps the file with its error and a retry button. Without it files stay idle, for forms that submit them natively.

  • autoUploaddefault true
    boolean

    Start uploads as files are added. Off, each row gets an upload button and the list an "Upload all" button.

  • maxConcurrentdefault 3
    number

    Uploads that run at once; the rest wait their turn.

  • sortabledefault true
    boolean

    Grid only: drag tiles to reorder (the first is the cover). A 250ms press on touch, Space and the arrow keys on a keyboard.

  • cameradefault false
    boolean

    On touch screens, adds a "Take photo" button that opens the back camera (capture="environment", images).

  • pastedefault true
    boolean

    Paste files and screenshots from the clipboard while focus is inside the component.

  • validate
    (file: File) => (string | null | undefined)

    Extra checks after type and size: return a message to reject the file with it (a duplicate, a too-small photo). formatFileSize(bytes, locale) is exported for messages.

  • onReject
    (rejections: { file: File; reason: "type" | "size" | "count" | "custom"; message: string }[]) => void

    Called with every rejected file of one add, together.

  • sizedefault "md"
    "sm" | "md" | "lg"

    Dropzone padding and icon (list), the attach button (compact) and the avatar: 64 / 96 / 128px.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg" | "full"

    Corners of the dropzone and photo tiles: 0 / 12 / 16 / 24px (full stays 24px). The avatar is always round.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Color of the drag-over tint, the progress bars and rings and the avatar badge.

  • colordefault ""
    string

    Any CSS color; replaces the tone. Leave empty to use the tone.

  • icon
    ReactNode

    The dropzone icon, CloudUpload by default.

  • labels
    Partial<FileUploadLabels>

    Every string, English by default: title ("Drag files here or"), browse, tapTitle, dropHere, hint (else built from accept, maxSize and maxFiles), camera, add, attach, change, choose, cover, remove, retry, cancel, start, uploadAll, uploading, queued, uploaded, canceled, failed, limitReached, images / videos / audio, wrongType, tooLarge(max), tooMany(max), maxSizeHint(max, single), maxFilesHint(max), the announcements announceUploaded / announceFailed / announceRejected / announceRemoved and the reordering texts dragInstructions, dragStart, dragOver, dragEnd, dragCancel.

  • localedefault ""
    string

    Formats file sizes ("2,4 MB" in ro-RO, "2.4 MB" in en-US). Empty uses the nearest LocaleProvider, then the page language.

  • labeldefault ""
    ReactNode

    Renders the field anatomy around the component. A click on it focuses the dropzone without opening the picker.

  • descriptiondefault ""
    ReactNode

    Helper text. In the list layout it becomes the dropzone's second line (unless labels.hint is set); elsewhere it shows under the component.

  • errordefault ""
    ReactNode

    The animated error under the component; also marks the dropzone invalid.

  • requireddefault false
    boolean

    The red * after the label; with name, the native form refuses to submit an empty list.

  • invaliddefault false
    boolean

    The invalid look without a message.

  • disableddefault false
    boolean

    Dims everything and blocks picking, dropping, pasting and the row actions.

  • namedefault ""
    string

    Names the hidden file input. Its files follow the list (through DataTransfer), so a native form or FormData submits exactly the files shown.

  • value
    UploadFile[]

    The files, controlled: { id, name, size, type, file?, url?, previewUrl?, status: "idle" | "uploading" | "done" | "error" | "canceled", progress, error?, response? }. Files that already exist on a server have url and status "done". Dropping a file mid-upload aborts it.

  • defaultValuedefault "[]"
    UploadFile[]

    The starting files when uncontrolled; a form reset returns to them.

  • onValueChange
    (files: UploadFile[]) => void

    The whole list after every add, remove, reorder, status change and throttled progress update.

  • id
    string

    The main trigger's id (dropzone, add tile, avatar or attach button), which the label points to.

  • fieldProps
    Partial<FieldProps>

    Anything else for the field wrapper: optional, labelAction, orientation, hideLabel.

  • classNames
    Partial<Record<"dropzone" | "icon" | "title" | "hint" | "camera" | "rejections" | "list" | "item" | "thumb" | "name" | "meta" | "progress" | "action" | "grid" | "tile" | "cover" | "remove" | "add" | "avatar" | "input" | "field" | "label" | "description" | "error" | "counter", string>>

    Extra classes for the inner parts and the field parts.

  • className
    string

    Extra classes for the root (the field root when a label, description or error is set).

Parts

  • formatFileSize

    function

    formatFileSize(bytes, locale?): a short, localized size like "2.4 MB" or "830 KB". Handy for validate messages and your own file lists.

  • acceptsFile

    function

    acceptsFile(file, accept?): the same check as the accept prop (extensions, exact MIME types and "image/*" wildcards), for validating files outside the component.

  • UploadFile

    type

    One entry of value: { id, name, size, type, file?, url?, previewUrl?, status, progress, error?, response? }.

  • UploadHandler

    type

    (file, { signal, onProgress }) => Promise<unknown>: your upload function. Pass signal to fetch so cancel and remove abort it.

  • FileRejection

    type

    A refused file passed to onReject: { file, reason: "type" | "size" | "count" | "custom", message }.

  • FileUploadProps

    type

    The props of FileUpload, for wrappers that pass them through.

Examples

Listing photos

grid with maxFiles={8}: drag or use Space and the arrows to reorder, the first tile is the cover.

Avatar

One round photo with a camera badge; a new pick replaces it and the ring shows the upload.

Compact in a chat composer

An attach button with small rows, controlled so Send can wait for the uploads.

Validation

Images only, 2 MB, at most 3, plus a duplicate check through validate. Try the three files.

Failures, retry and cancel

Every third upload fails halfway; maxConcurrent={2} keeps the rest waiting.

Camera on phones

camera adds a Take photo button on touch screens; desktops keep the plain dropzone.

Native form

name="documents" and no upload: the files ride along in FormData, reset empties the list.

Info tone

radius="lg" and the info tone on the progress, sizes as 1.8 MB.

Dependencies

@dnd-kit/core@dnd-kit/sortable@dnd-kit/utilitiesmotionlucide-reactfieldbuttonbadgenumber-formatuse-localemotiontonesuse-media-queryuse-controllable-stateui-config

Files

  • components/ui/file-upload.tsx

FAQ

Is the React File Upload component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install File Upload?

Run npx wingo-ui@latest add file-upload. It also adds the Wingo UI items it builds on (Field, Button, Badge, Number format, useLocale and 5 more) and the npm packages @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/file-upload.

How does File Upload behave on phones?

On a phone the dropzone reads "Tap to choose files", remove buttons on photos are always visible, a 250ms press starts reordering so the page still scrolls, every action has a 44px target, and with camera on a "Take photo" button opens the camera.

See also

FieldImage CropperFile typeslibSignature PadNumber formatlib

Used in

Avatar UploadFile managerCSV ImportFile SystemPrompt InputCRM Contact PagePost Listing WizardCareers PageJob Detail Page

More React input components

Filter BuilderInputInput GroupListboxMention InputMonth Picker
All 40 React input components

Articles using this component

  • React File Manager: Folders, Uploads and the Backend API

    Component guides · Nov 6, 2026 · 10 min read

  • React PDF Viewer: Search, Highlights, Real Annotations

    Component guides · Oct 19, 2026 · 9 min read

  • React CSV Importer: Upload, Map Columns, Fix Rows, Import

    Component guides · Oct 16, 2026 · 7 min read

  • React File Upload: Drag and Drop, Paste, Camera, Progress

    Component guides · Oct 16, 2026 · 11 min read

PreviousEmoji PickerNext Filter Builder

Post a listing

Step 3 of 4 · Documents

  • purchase-agreement.pdf1.8 MB · PDF
  • property-deed.pdf612 KB · PDF

autoUpload=true · maxConcurrent=3 · multiple=true · paste=true · camera=false