Zum Inhalt springen
    WingoUI
    KomponentenVorlagenDokuPreiseKI-AgentenBlogChangelog
    1. Startseite
    2. Blog
    3. Komponenten-Guides
    4. React File Upload: Drag and Drop, Kamera, Fortschritt
    Read in English
    1. Blog
    2. Komponenten-Guides
    3. React File Upload: Drag and Drop, Kamera, Fortschritt
    Komponenten-Guides
    Komponenten-Guides

    React File Upload: Drag and Drop, Kamera, Fortschritt

    SR
    Serban Rusu · Gründer von Wingo UI
    16. Okt. 2026 · 12 Min. Lesezeit

    Auf dieser Seite

    0%
    1. Was muss ein React File Upload können?
    2. Wie funktioniert Drag and Drop in einem React File Upload?
    3. Wie zeigst du bei einem React File Upload einen Fortschrittsbalken?
    4. Wie gehst du mit Einfügen und der Handykamera um?
    5. Wie validierst du Dateien vor dem Upload?
    6. Wie lädst du Dateien zu einem Route Handler in Next.js hoch?
      1. Warum scheitern Uploads über 4,5 MB auf Vercel?
      2. Kannst du die Dateien auch mit einem normalen Formular absenden?
    7. Wie zeigst du Dateien an, die schon hochgeladen sind?
    8. Wie schneidest du ein Foto vor dem Upload zu?
    9. Ist der File Upload per Tastatur und auf dem Handy barrierefrei?
    10. Solltest du stattdessen einen shadcn File Upload nehmen?
    11. Nächster Schritt
    12. Häufige Fragen
      1. Wie zeige ich in React den Upload-Fortschritt an?
      2. Wie füge ich Bilder in einen React File Upload ein?
      3. Wie öffne ich auf dem Handy die Kamera aus einem File Input?
      4. Reicht das accept-Attribut, um Uploads zu validieren?
      5. Gibt es eine File-Upload-Komponente für shadcn?
      6. Warum scheitern große Uploads auf Vercel?

    Kurz gesagt

    Ein guter React File Upload nimmt Dateien auf vier Wegen an (Drag and Drop, die Dateiauswahl, Einfügen und die Handykamera), prüft jede Datei vor dem Upload und listet abgelehnte Dateien mit einem Grund auf, statt sie still zu verwerfen. Den Fortschritt misst du mit XMLHttpRequest, weil dessen upload-Objekt Progress-Events feuert und fetch keine hat; über ein AbortSignal stoppen Abbrechen und Entfernen den Request dann wirklich. Der File Upload von Wingo UI erledigt all das mit einer einzigen upload-Funktion als Prop, und der Server muss Typ und Größe trotzdem noch einmal prüfen.

    Veröffentlicht am 16. Okt. 2026

    Ein File Input wirkt fertig, sobald sich die Dateiauswahl öffnet. Dann kommen die echten Wünsche: Dateien vom Desktop hineinziehen, einen Screenshot einfügen, auf dem Handy ein Foto machen, den Fortschritt jeder Datei sehen, den Upload der falschen abbrechen. Ein React File Upload muss alle vier Eingabewege abdecken, jede Datei prüfen, bevor sie den Browser verlässt, und klar sagen, warum eine Datei abgelehnt wurde. Dieser Guide zeigt, was dazugehört, wie Fortschritt und Abbrechen unter der Haube funktionieren, und baut ein komplettes Beispiel mit einem Route Handler in Next.js, inklusive der Größenlimits für den Body, an denen Uploads in Produktion scheitern. Die Beispiele nutzen die File-Upload-Komponente von Wingo UI, der Bibliothek, die wir bauen. Wir sind also nicht neutral; für Fakten von außen verlinken wir die Quellen.

    Was muss ein React File Upload können?

    Vier Eingabewege, drei Prüfungen und einen Status pro Datei. Das ist die Liste, nach der wir gebaut haben, und ein fairer Test für jede Bibliothek oder eine selbst geschriebene Lösung:

    • Drag and Drop. Eine Datei, die irgendwo über die Seite gezogen wird, soll die Dropzone hervorheben, und ein Drop daneben soll nichts tun. Sonst öffnet der Browser die Datei, und das Formular ist weg.
    • Durchsuchen. Ein Klick, Enter oder Leertaste auf der Dropzone öffnet die native Dateiauswahl. Drag and Drop ist das Extra. Die Dateiauswahl ist die Basis, die überall funktioniert.
    • Einfügen. Strg+V oder Cmd+V mit dem Fokus in der Komponente fügt kopierte Dateien und Screenshots hinzu.
    • Kamera. Auf dem Handy ein Button „Foto aufnehmen“, der direkt die Kamera öffnet.
    • Prüfungen. Erlaubte Typen, ein Größenlimit und eine maximale Anzahl, dazu deine eigenen Regeln, mit einem sichtbaren Grund für jede abgelehnte Datei.
    • Status. Eine Vorschau für Bilder, ein Fortschrittsbalken pro Datei, Abbrechen, Wiederholen und Entfernen.

    Installier die Komponente und gib ihr eine accept-Liste:

    bash
    npx wingo-ui@latest add file-upload
    tsx
    import { FileUpload } from "@/components/ui/file-upload";
    export function Documents() {
    return (
    <FileUpload
    label="Unterlagen zur Immobilie"
    description="PDFs oder Bilder, je bis 10 MB"
    accept="image/*,.pdf"
    maxFiles={8}
    />
    );
    }

    Ohne upload-Funktion bleiben die Dateien einfach in der Liste liegen, ohne hochzuladen. Genau das willst du bei einem Formular, das sie nativ absendet (mehr dazu weiter unten).

    Zieh ein PDF oder ein Foto vom Desktop über die Seite, fokussier dann die Dropzone und füg einen Screenshot ein: Jede Datei bekommt eine Zeile mit ihrer Größe und einem Fortschrittsbalken
    $ npx wingo-ui@latest add file-upload
    ProDoku: File Upload

    Wie funktioniert Drag and Drop in einem React File Upload?

    Der Browser feuert dragenter, dragover, dragleave und drop, und die abgelegten Dateien kommen in event.dataTransfer an. An drei Details scheitert selbst geschriebener Code für React File Upload Drag and Drop immer wieder. Erstens muss dragover preventDefault() aufrufen, sonst feuert das Drop-Event nie. Zweitens öffnet der Browser bei einem Drop außerhalb deiner Zone die Datei und verlässt die Seite. Drittens feuern dragenter und dragleave bei jedem Kindelement, über das der Zeiger fährt, erneut, deshalb flackert ein einfacher Boolean. Zähl die Enter- und Leave-Events mit und nimm die Hervorhebung erst zurück, wenn der Zähler wieder bei null ist.

    Unsere Komponente löst die ersten beiden Punkte direkt am window: Ein Drag mit Dateien hebt jede Dropzone auf der Seite mit „Drop to upload“ hervor, und ein Drop daneben wird abgefangen. Vorher prüft sie, ob dataTransfer.types "Files" enthält. Wer Text oder einen Link über die Seite zieht, löst also nichts aus. Ordner überspringt sie ebenfalls. Ein abgelegter Ordner erscheint in dataTransfer.items als Eintrag, bei dem webkitGetAsEntry() isDirectory meldet, und getAsFile() liefert dir nicht seinen Inhalt. Die Komponente lässt ihn weg, statt eine kaputte Zeile anzulegen.

    Wie zeigst du bei einem React File Upload einen Fortschrittsbalken?

    Für einen Upload mit Fortschrittsbalken nimmst du XMLHttpRequest. Sein upload-Objekt feuert progress-Events mit loaded und total Bytes. fetch hat kein Progress-Event für den Upload, ein Upload mit fetch kann also nur einen Spinner zeigen. Die MDN-Seite zu XMLHttpRequest.upload (opens in a new tab) (geprüft im Oktober 2026) nennt noch zwei Fallen. Listener müssen vor send() registriert sein, sonst feuern keine Upload-Events, und Browser brauchen sie oft schon vor open(), also setz sie zuerst. Außerdem ist ein Cross-Origin-Request mit Upload-Listener kein einfacher Request („simple request“) mehr, und das kostet einen CORS-Preflight.

    Die Komponente nimmt eine einzige Funktion, upload(file, { signal, onProgress }). Du rufst onProgress mit einem Anteil von 0 bis 1 auf und verbindest signal mit dem Request, damit Abbrechen, Entfernen und Unmount den Request stoppen. Speicher das als upload-file.ts; die späteren Beispiele importieren es:

    ts
    import type { UploadHandler } from "@/components/ui/file-upload";
    export const uploadFile: UploadHandler = (file, { signal, onProgress }) =>
    new Promise<{ url: string }>((resolve, reject) => {
    const request = new XMLHttpRequest();
    // Listener vor open(): manche Browser verwerfen sonst die Upload-Events
    request.upload.onprogress = (event) => {
    if (event.lengthComputable) onProgress(event.loaded / event.total);
    };
    request.onload = () => {
    // responseType "json" liefert bei einem kaputten Body null, statt zu werfen
    if (request.status < 300 && request.response) resolve(request.response);
    else reject(new Error(request.response?.error ?? "Der Upload ist fehlgeschlagen"));
    };
    request.onerror = () => reject(new Error("Keine Verbindung"));
    request.onabort = () => reject(new DOMException("Abgebrochen", "AbortError"));
    signal.addEventListener("abort", () => request.abort(), { once: true });
    request.open("POST", "/api/upload");
    request.responseType = "json";
    const body = new FormData();
    body.append("file", file);
    request.send(body);
    });

    Der onload-Handler ist wichtiger, als er aussieht. Ein JSON.parse an dieser Stelle wirft innerhalb eines Event-Handlers, wenn der Server mit einer HTML-Fehlerseite antwortet. Das Promise bleibt dann für immer offen, und der Spinner in der Zeile dreht sich endlos. Mit responseType = "json" ist ein kaputter Body null, und die eigene error-Meldung des Servers landet in der Zeile.

    Fortschritts-Updates sind auf ein Rendern pro 100ms und Datei gedrosselt, damit eine schnelle Verbindung die Liste nicht hunderte Male pro Sekunde neu rendert. Standardmäßig laden höchstens drei Dateien gleichzeitig hoch (maxConcurrent), der Rest wartet, bis er dran ist. Der Wert, mit dem das Promise erfüllt wird, landet in der response der Datei. Wird das Promise abgelehnt, bleibt die Datei mit ihrer Fehlermeldung und einem Button zum Wiederholen in der Liste. Setz autoUpload={false}, wenn deine Nutzer die Liste erst prüfen sollen: Jede Zeile bekommt dann einen Upload-Button und die Liste einen Button „Upload all“.

    Wie gehst du mit Einfügen und der Handykamera um?

    Wer in einem React File Upload einen Screenshot einfügen will, liest event.clipboardData.files. Diese Liste enthält kopierte Dateien und Screenshots, die direkt in der Zwischenablage gelandet sind. Der Haken ist, wo du den Listener anhängst. Ein onPaste auf der Dropzone verpasst ein Einfügen, während der Fokus auf dem Entfernen-Button einer Zeile sitzt. Deshalb lauscht die Komponente am document und reagiert nur, wenn document.activeElement in ihrem Root liegt. preventDefault() ruft sie nur auf, wenn die Zwischenablage Dateien enthält. So lässt sich Text in ein anderes Eingabefeld auf der Seite weiter ganz normal einfügen. paste ist standardmäßig an; mit paste={false} schaltest du es ab.

    Die Kamera ist ein zweites, verstecktes Input-Element mit capture="environment" und accept="image/*". Laut der MDN-Seite zum capture-Attribut (opens in a new tab) fordert environment die nach außen gerichtete Kamera an und user die Frontkamera, und ein Desktop-Browser zeigt stattdessen meist die normale Dateiauswahl. Deshalb ergänzt camera seinen Button „Take photo“ nur auf Touchscreens:

    tsx
    <FileUpload variant="grid" accept="image/*" maxFiles={12} camera upload={uploadFile} />

    Das Grid-Layout ist für Fotos gebaut. Die erste Kachel ist das Titelbild, und du sortierst um, indem du ziehst, auf Touchscreens nach 250ms Gedrückthalten, damit die Seite weiter scrollbar bleibt, oder mit Leertaste und den Pfeiltasten. Warum diese Verzögerung wichtig ist, erklärt unser Beitrag über dnd-kit Drag auf Touchscreens.

    Füg ein paar Fotos hinzu und zieh dann eine Kachel an die erste Stelle, um sie zum Titelbild zu machen; auf dem Handy hältst du die Kachel vor dem Ziehen kurz gedrückt
    $ npx wingo-ui@latest add file-upload
    ProDoku: File Upload

    Wie validierst du Dateien vor dem Upload?

    Für die üblichen Regeln nimmst du accept, maxSize und maxFiles, für eigene validate. maxSize steht standardmäßig auf 10 MB; setz es auf 0, wenn du wirklich kein Limit willst. Jede abgelehnte Datei steht unter der Dropzone mit einer Meldung („You can add up to 6 files“) und geht mit einem Code für den Grund ("type", "size", "count" oder "custom") an onReject. Nichts verschwindet also ohne Erklärung. Die Prüfungen laufen pro Datei in dieser Reihenfolge: Typ, dann Größe, dann dein validate, dann die Anzahl. Eine Datei, die an zwei Regeln scheitert, bekommt also nur die erste Meldung. accept vergleicht Endungen ohne Rücksicht auf Groß- und Kleinschreibung und versteht MIME-Wildcards wie image/*. acceptsFile(file, accept) führt dieselbe Prüfung außerhalb der Komponente aus, und formatFileSize(bytes, locale) formatiert Größen für deine Meldungen.

    Versteh das alles als Feedback. accept filtert nur die Dateiauswahl, und jeder kann beliebige Dateien an deinen Endpoint schicken, also prüft der Server noch einmal. Beachte auch: file.type kommt auf dem Client aus der Dateiendung und auf dem Server aus dem Multipart-Header. Beides sind Behauptungen des Absenders. Wenn der Typ für die Sicherheit zählt, lies auf dem Server die ersten Bytes der Datei.

    Wie lädst du Dateien zu einem Route Handler in Next.js hoch?

    Schick jede Datei an einen Route Handler, prüf sie dort noch einmal und speicher sie unter einem Namen, den du selbst erzeugst. Der Client: Dokumente, eine Prüfung auf Duplikate, ein Limit von 10 MB und ein Callback für abgelehnte Dateien.

    tsx
    "use client";
    import { useState } from "react";
    import { FileUpload, formatFileSize, type UploadFile } from "@/components/ui/file-upload";
    import { uploadFile } from "./upload-file";
    export function ClaimDocuments() {
    const [files, setFiles] = useState<UploadFile[]>([]);
    return (
    <FileUpload
    label="Unterlagen zum Schadensfall"
    description="PDFs oder Fotos, je bis 10 MB"
    accept="image/*,.pdf"
    maxSize={10 * 1024 * 1024}
    maxFiles={6}
    camera
    value={files}
    onValueChange={setFiles}
    upload={uploadFile}
    validate={(file) =>
    files.some((f) => f.name === file.name && f.size === file.size)
    ? `${file.name} (${formatFileSize(file.size, "de-DE")}) ist schon in der Liste`
    : null
    }
    onReject={(rejections) => console.warn(rejections.map((r) => r.message))}
    locale="de-DE"
    />
    );
    }

    Der Server: ein Route Handler in app/api/upload/route.ts, der die Prüfungen wiederholt, bevor er irgendetwas speichert.

    ts
    import { saveToStorage } from "@/lib/storage"; // dein eigener Helper für S3, R2 oder die Festplatte
    const MAX_SIZE = 10 * 1024 * 1024;
    // erlaubte Typen und die Endung, mit der jeder gespeichert wird
    const EXTENSIONS: Record<string, string> = {
    "application/pdf": "pdf",
    "image/jpeg": "jpg",
    "image/png": "png",
    "image/webp": "webp",
    "image/heic": "heic",
    };
    export async function POST(request: Request) {
    const form = await request.formData();
    const file = form.get("file");
    if (!(file instanceof File)) return Response.json({ error: "Keine Datei" }, { status: 400 });
    if (file.size > MAX_SIZE) return Response.json({ error: "Datei zu groß" }, { status: 413 });
    const extension = EXTENSIONS[file.type];
    if (!extension) return Response.json({ error: "Dateityp nicht erlaubt" }, { status: 415 });
    const bytes = new Uint8Array(await file.arrayBuffer());
    const key = `${crypto.randomUUID()}.${extension}`;
    await saveToStorage(key, bytes);
    return Response.json({ url: `/uploads/${key}` });
    }

    Der Key nutzt nie file.name. Dieser Name ist, was auch immer der Client geschickt hat, und ../../app.config.js darf nie in einem Pfad auf der Festplatte landen; die Endung kommt stattdessen aus dem erlaubten Typ. Der Body { error } ist genau das, was die upload-Funktion von oben in der fehlgeschlagenen Zeile anzeigt.

    Ein Route Handler ist der richtige Ort für den Upload-Endpoint. Laut der Next.js-Doku zu Server Actions (opens in a new tab) (geprüft im Oktober 2026) sind Requests an Actions standardmäßig auf 1MB begrenzt (serverActions.bodySizeLimit hebt das Limit an), und eine Action liefert ohnehin keinen Upload-Fortschritt.

    Warum scheitern Uploads über 4,5 MB auf Vercel?

    Das Hosting setzt ein eigenes Limit. Vercels Seite zu den Limits von Functions (opens in a new tab) (geprüft im Oktober 2026) begrenzt den Request-Body einer Vercel Function auf 4,5 MB und beantwortet größere Bodys mit einem 413 FUNCTION_PAYLOAD_TOO_LARGE. Die Route oben mit ihren 10 MB läuft also lokal und scheitert dort in Produktion. Die Lösung: Halt die Datei von der Function fern. Die Route gibt eine vorsignierte URL deines Storage zurück (S3 und R2 bieten das beide an), und die upload-Funktion schickt die Datei direkt dorthin. Fortschritt und Abbrechen funktionieren weiter, denn es ist immer noch ein XMLHttpRequest:

    ts
    import type { UploadHandler } from "@/components/ui/file-upload";
    export const uploadDirect: UploadHandler = async (file, { signal, onProgress }) => {
    // deine Route: prüft Typ und Größe, gibt eine vorsignierte PUT-URL und die öffentliche URL zurück
    const response = await fetch("/api/upload-url", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: file.name, type: file.type, size: file.size }),
    signal,
    });
    if (!response.ok) throw new Error("Der Upload wurde abgelehnt");
    const { uploadUrl, url } = (await response.json()) as { uploadUrl: string; url: string };
    await new Promise<void>((resolve, reject) => {
    const request = new XMLHttpRequest();
    request.upload.onprogress = (event) => {
    if (event.lengthComputable) onProgress(event.loaded / event.total);
    };
    request.onload = () => (request.status < 300 ? resolve() : reject(new Error("Der Upload ist fehlgeschlagen")));
    request.onerror = () => reject(new Error("Keine Verbindung"));
    request.onabort = () => reject(new DOMException("Abgebrochen", "AbortError"));
    signal.addEventListener("abort", () => request.abort(), { once: true });
    request.open("PUT", uploadUrl);
    request.setRequestHeader("Content-Type", file.type);
    request.send(file);
    });
    return { url };
    };

    Der Bucket braucht eine CORS-Regel, die PUT und den Header Content-Type von deinem Origin erlaubt. Durch den Upload-Listener ist der Request kein einfacher Request mehr, also schickt der Browser vorher einen Preflight. Fehlt die Regel, siehst du nur den Fehler „Keine Verbindung“ aus onerror, und die eigentliche Ursache steht nur in der Browser-Konsole. Wenn deine vorsignierte URL einen Content-Type mitsigniert, schick genau diesen, sonst lehnt der Storage die Signatur ab.

    Kannst du die Dateien auch mit einem normalen Formular absenden?

    Gib der Komponente einen name und lass upload weg. Sie hält einen versteckten File Input, dessen Dateien über DataTransfer der Liste folgen. Ein nativer Formular-Submit oder new FormData(form) schickt also genau die Dateien, die gerade angezeigt werden, und required blockiert ein leeres Absenden. Unser Guide zu React-Formularkomponenten behandelt die Anatomie des Felds drumherum: Label, Beschreibung und die animierte Fehlermeldung.

    Wie zeigst du Dateien an, die schon hochgeladen sind?

    Übergib sie als defaultValue oder value mit einer url und status: "done", dann erscheinen sie als fertige Zeilen. Wenn die Dateien auf einem eigenen Screen liegen, etwa in einem Kundenordner oder als E-Mail-Anhänge, passt die File-List-Komponente besser: Zeilen, Vorschaukacheln oder Chips, Download und Löschen mit Bestätigungsdialog, Auswahl und Fortschritt für Dateien, die noch hochladen. Das UploadFile[], das File Upload meldet, passt unverändert in ihre Prop files.

    Wie schneidest du ein Foto vor dem Upload zu?

    Setz den Image Cropper zwischen Auswahl und Upload. Er nimmt ein File-Objekt als src, bietet Presets für das Seitenverhältnis, Zoom per Pinch oder Mausrad und Drehung, und gibt dir über setApi eine API, deren toBlob ein JPEG, PNG oder WebP exportiert:

    tsx
    "use client";
    import { useState } from "react";
    import { Button } from "@/components/ui/button";
    import { ImageCropper, type ImageCropperApi } from "@/components/ui/image-cropper";
    export function CoverPhoto({ photo, onSave }: { photo: File; onSave: (file: File) => void }) {
    const [api, setApi] = useState<ImageCropperApi | null>(null);
    const save = async () => {
    if (!api) return;
    const blob = await api.toBlob({ type: "image/jpeg", quality: 0.9, maxSize: 1600 });
    onSave(new File([blob], "cover.jpg", { type: "image/jpeg" }));
    };
    return (
    <div className="flex max-w-xl flex-col gap-4">
    <ImageCropper src={photo} defaultAspect={4 / 3} setApi={setApi} />
    <Button onClick={save}>Zuschnitt speichern</Button>
    </div>
    );
    }

    maxSize begrenzt die längste Seite und skaliert nie hoch, ein Handyfoto mit 12 Megapixeln geht also nicht in voller Größe über die Leitung. Der Cropper exportiert über ein Canvas, deshalb enthält das Ergebnis keine EXIF-Daten. Damit verschwindet auch der GPS-Standort, den ein Handy in Fotos schreibt. Für ein Profilfoto kombiniert Avatar Upload Auswahl, runden Zuschnitt und Upload schon in einer Komponente. Der Guide zum React Image Cropper behandelt Zoom, Drehung, die Exportgröße und diesen Avatar-Ablauf genauer.

    Zieh das Foto unter dem Rahmen zurecht, zoom per Pinch oder Scrollen, stell dann das Verhältnis auf 1:1 und exportier den Zuschnitt
    $ npx wingo-ui@latest add image-cropper
    ProDoku: Image Cropper

    Ist der File Upload per Tastatur und auf dem Handy barrierefrei?

    Ja. Die Dropzone ist ein echtes fokussierbares Element: Tab erreicht sie, Enter oder Leertaste öffnet die Dateiauswahl, und ein Klick aufs Label fokussiert sie, ohne die Auswahl zu öffnen. Eine Live-Region im Modus „polite“ sagt hochgeladene, fehlgeschlagene, abgelehnte und entfernte Dateien an, dazu jedes Umsortieren im Grid. Auf dem Handy steht in der Dropzone „Tap to choose files“, die Entfernen-Buttons auf Fotos sind immer sichtbar, statt erst beim Hover zu erscheinen, und jede Aktion hat eine Trefferfläche von 44px, die Größe, die unser Guide zu Touch-Zielen erklärt.

    Solltest du stattdessen einen shadcn File Upload nehmen?

    Stand Oktober 2026 hat shadcn/ui keine eigene Dropzone. Seine Attachment-Komponente (opens in a new tab) zeigt eine Datei mit ihrem Upload-Status und Aktionen an, aber Dateiauswahl, Drop-Handling und die Berechnung des Fortschritts baust du selbst. Ein shadcn File Upload heißt also meist: ein Item aus einer Community-Registry. Der File Upload von Dice UI (opens in a new tab) ist ein zusammensetzbares Set aus Teilen (Dropzone, Trigger, Liste, Vorschau pro Item, Fortschritt pro Item) mit Validierung und wird mit npx shadcn@latest add @diceui/file-upload installiert. Wenn du diese Teile selbst zusammensetzen willst, ist das eine gute Wahl. Der File Upload von Wingo UI ist ein einziges Element, das du über Props konfigurierst, mit vier Layouts (list, grid, avatar, compact), Einfügen, dem Kamera-Button, Umsortieren im Grid, einer Upload-Warteschlange und einem Fallback für native Formulare, alles eingebaut.

    Nächster Schritt

    Installier alles mit npx wingo-ui@latest add file-upload file-list image-cropper. Alle drei gehören zu Wingo UI Pro (Preise); die Doku zu File Upload hat den Playground und jede Prop. Weitere Komponenten-Guides findest du in der Kategorie Komponenten.

    Komponenten in diesem Artikel

    • File Upload

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

      Pro
    • File List

      A React file list for attachments and documents: rows, preview tiles or chips with type icons, thumbnails, upload progress and file actions.

      Pro
    • Image Cropper

      A React image cropper for photos before upload: aspect presets, pinch or wheel zoom, rotation, a thirds grid and a JPEG, PNG or WebP export.

      Pro

    Häufige Fragen

    Wie zeige ich in React den Upload-Fortschritt an?

    Schick die Datei mit XMLHttpRequest und werte request.upload.onprogress aus, das die geladenen und die gesamten Bytes meldet. fetch hat kein Progress-Event für den Upload, ein Upload mit fetch kann also nur einen Spinner zeigen. Registriere den Listener vor open(), denn laut MDN lassen Browser Upload-Events oft aus, wenn der Listener erst später dazukommt.

    Wie füge ich Bilder in einen React File Upload ein?

    Reagier auf das paste-Event und lies event.clipboardData.files, dort liegen kopierte Dateien und Screenshots. Häng den Listener ans document und reagiere nur, wenn document.activeElement in deiner Komponente liegt. So klappt das Einfügen, egal ob der Fokus auf der Dropzone oder auf einem Button in einer Zeile sitzt.

    Wie öffne ich auf dem Handy die Kamera aus einem File Input?

    Setz capture="environment" für die Rückkamera oder capture="user" für die Frontkamera, zusammen mit accept="image/*". Laut MDN zeigen Desktop-Browser stattdessen meist die normale Dateiauswahl, biete die Kamera auf Touchscreens also als zusätzlichen Button an.

    Reicht das accept-Attribut, um Uploads zu validieren?

    Nein. accept filtert nur die Dateiauswahl; eine hineingezogene oder eingefügte Datei umgeht es, und jeder kann beliebige Dateien an deinen Endpoint schicken. Prüf Typ und Größe in der Komponente für schnelles Feedback und noch einmal auf dem Server, bevor du die Datei speicherst.

    Gibt es eine File-Upload-Komponente für shadcn?

    Stand Oktober 2026 hat shadcn/ui eine Attachment-Komponente, die Dateien und ihren Upload-Status anzeigt, aber keine Dropzone und keine Dateiauswahl. Community-Registries wie Dice UI füllen die Lücke, und der File Upload von Wingo UI lässt sich genauso als Quellcode installieren, mit npx wingo-ui@latest add file-upload.

    Warum scheitern große Uploads auf Vercel?

    Vercel Functions nehmen Request-Bodys bis 4,5 MB an und beantworten alles darüber mit einem Fehler 413. Für größere Dateien gibt deine Route eine vorsignierte Storage-URL zurück, und der Browser schickt die Datei direkt in den Storage.

    • Datei-Upload
    • Formulare
    • Drag and Drop
    • Mobile UI
    • Barrierefreiheit

    Teilen

    SR

    Über den Autor

    Serban Rusu

    Gründer von Wingo UI

    Serban Rusu hat Wingo UI gegründet. Er baut die Komponenten, die CLI und den MCP-Server und schreibt über React-UIs für Smartphones und KI-Agents.

    Mehr von Serban
    Weiterlesen

    Ähnliche Artikel

    Alle Artikel
    Komponenten-Guides

    React Image Cropper: Zuschnitt vor dem Upload, Zoom, Drehung

    Bau einen React Image Cropper mit Pinch-Zoom und Drehung, der vor dem Upload zuschneidet, ein WebP in Zielgröße liefert und Avatare mit Fortschritt hochlädt.

    SRSerban Rusu·19. Okt. 2026·9 Min. Lesezeit
    Komponenten-Guides

    React PDF Viewer: Suche, Highlights, echte Annotationen

    Bau einen React PDF Viewer auf PDFium mit Suche, Thumbnails und Highlights, exportierbar als echte PDF-Annotationen, plus Vergleich mit react-pdf und pdf.js.

    SRSerban Rusu·19. Okt. 2026·9 Min. Lesezeit
    Komponenten-Guides

    React CSV Importer: Spalten zuordnen, Zeilen korrigieren

    Bau einen React CSV Importer in vier Schritten: CSV oder Excel hochladen, Spalten zuordnen, fehlerhafte Zeilen direkt korrigieren, mit Fortschritt importieren.

    SRSerban Rusu·16. Okt. 2026·7 Min. Lesezeit
    Newsletter

    Neue Artikel per E-Mail

    Neue Guides, Tutorials und Vergleiche aus dem Wingo UI Blog, sobald sie erscheinen. Die meisten Artikel sind auf Englisch.

    Kein Spam. Du kannst dich jederzeit abmelden.

    WingoUI

    Animierte, konfigurierbare, mobile-first React-Komponenten und Blöcke. Kopier den Quellcode, pass ihn an und lass deinen Coding-Agenten damit bauen.

    Produkt

    • Komponenten
    • Vorlagen
    • Preise
    • KI-Agenten
    • Theme-Generator

    Ressourcen

    • Doku
    • Blog
    • Changelog
    • Changelog-RSS
    • llms.txt

    Rechtliches

    • AGB
    • Datenschutz
    • Rückerstattung
    • Lizenz
    • Impressum

    Komponenten-Kategorien

    • Buttons & Actions
    • Inputs
    • Forms
    • Navigation
    • Overlays
    • Feedback
    • Data Display
    • Tables & Lists
    • Charts & Stats
    • Layout
    • Media
    • AI Kit
    • Text & Effects
    • Mobile
    • Commerce
    • Marketing Sections
    • Blocks
    • Hooks & Utilities

    © 2026 Wingo UI

    Wingo UI