React Prompt Input: Enter, IME, Dateien, Stop und Diktat

Ein React Prompt Input ist das Eingabefeld unten in jedem KI-Chat, und die erste Version wirkt immer fertig. Eine Textarea, ein Enter-Handler und ein Senden-Button laufen auf deinem Laptop. Dann schickt jemand, der Japanisch tippt, einen halben Satz ab, sobald er ein Wort bestätigt. Auf dem Smartphone lässt sich keine zweite Zeile schreiben. Ein PDF mit 30 MB scheitert ohne Meldung. Während eine lange Antwort streamt, fehlt der Stop-Button, und auf iOS rutscht das Feld hinter die Tastatur. Dieser Guide geht all diese Details durch, mit dem Code, den wir im Prompt Input von Wingo UI ausliefern. Wir bauen diese Bibliothek und sind daher nicht neutral; externe Fakten sind mit ihren Quellen verlinkt.
Was soll Enter in einem React Prompt Input tun?
Auf einer Hardware-Tastatur sendet Enter, und Shift+Enter fügt eine Zeile ein. Die Doku zum Chat Composer von MUI (opens in a new tab) und der Prompt Input von AI Elements (opens in a new tab) von Vercel beschreiben beide dieses Standardverhalten (Stand Oktober 2026). Drei Ausnahmen sind wichtig:
- Touch-Tastaturen. Eine Smartphone-Tastatur hat kein Shift+Enter. Sendet Enter dort, kann niemand eine Liste schreiben. Prompt Input prüft
(pointer: coarse)und lässt Enter auf Touchscreens eine neue Zeile einfügen. Gesendet wird dort mit dem runden Button. - Lange Prompts. Mit
submitShortcut="mod+enter"fügt Enter eine neue Zeile ein, und Cmd/Ctrl+Enter sendet. Cmd/Ctrl+Enter sendet in beiden Modi. - Tastenwiederholung. Wer Enter gedrückt hält, löst keydown immer wieder aus. Der Handler ignoriert deshalb
event.repeat.
Das Attribut enterkeyhint (opens in a new tab) ändert nur die Beschriftung oder das Symbol der Enter-Taste, nie ihr Verhalten. Es muss also zu dem passen, was dein Handler tut. Prompt Input setzt enterKeyHint="send", wenn Enter sendet, und "enter" auf Touch-Geräten, wo Enter eine Zeile einfügt. Ein versteckter Hinweis „Shift + Enter for a new line“, verknüpft über aria-describedby, erklärt Screenreader-Nutzern die Regel.
Wie verhinderst du, dass Enter während der IME-Eingabe sendet?
Kehr im Key-Handler sofort zurück, solange eine Komposition offen ist. Eingabemethoden für Japanisch, Chinesisch und Koreanisch bestätigen eine Umwandlung mit Enter, und dieses Enter landet in deinem keydown-Handler. Die keydown-Referenz von MDN (opens in a new tab) erklärt, warum isComposing allein nicht reicht: Beim keydown, der den IME öffnet oder schließt, kann es false sein, während keyCode trotzdem 229 ist. Der Guard prüft deshalb beides und zusätzlich die Taste Process, die manche Browser melden:
Bind ihn mit onKeyDown={(event) => onPromptKeyDown(event, send)} ein und teste ihn vor dem Release mit einer japanischen oder Pinyin-Eingabemethode. Auf macOS öffnen auch Tottasten für Akzente (Option+E, dann E) eine kurze Komposition, derselbe Guard deckt sie also mit ab. TypeScript markiert keyCode als veraltet. Behalte es trotzdem, denn bei diesen Grenzfällen ist es das einzige verlässliche Signal.
Wie sollen Anhänge im Prompt Input funktionieren?
Schick jeden Weg, auf dem eine Datei hereinkommt, durch einen einzigen Validator, zeig den Fortschritt pro Datei und lass Leute abbrechen. Dateien kommen auf drei Wegen ins Feld:
- Der +-Button. Er öffnet „Upload files“ und auf Touch-Geräten „Take a photo“ (
capture="environment", abschalten mitcamera={false}). - Drag-and-drop. Der Composer nimmt Dateien an, egal wo sie auf ihm abgelegt werden, und zeigt ein gestricheltes Overlay, damit klar ist, wo sie landen.
- Einfügen. Ein eingefügter Screenshot wird zum Anhang. Eingefügter Text bleibt Text.
Alle drei laufen durch readAttachments. Die Funktion prüft den Dateityp anhand von MIME-Typ und Endung gegen accept, die Größe gegen maxFileSize (standardmäßig 20 MB) und die Anzahl gegen maxFiles (10). Abgelehnte Dateien verschwinden nicht einfach, sondern bekommen eine klare Meldung („Invoice.pdf is larger than 20 MB“).
Dann der Upload. Übergib onUpload(file, { signal, onProgress }), und jeder Anhang zeigt einen Fortschrittsring. Bei einem Fehler erscheint Retry, und wer eine Datei entfernt, bricht ihre Anfrage über das Signal ab. Ruf onProgress so oft auf, wie du willst: Der Composer rendert höchstens einmal pro 100ms und Datei. Eine Stolperfalle: fetch liefert keinen Upload-Fortschritt, ein echter Fortschrittsring braucht also XMLHttpRequest:
Gesendet wird erst, wenn keine Datei mehr hochlädt, und fehlgeschlagene Dateien bleiben aus der Nachricht draußen. Ohne onUpload bleiben Anhänge lokal, und du lädst sie selbst in onSubmit hoch. Löst dein Upload mit einem URL-String oder { url } auf, wird die url des Anhangs automatisch gesetzt. Für Tastaturnutzer gibt es noch einen Shortcut: Backspace mit dem Cursor ganz am Anfang des Felds fokussiert den Entfernen-Button des letzten Anhangs, und ein zweites Backspace entfernt ihn.
Wie baust du einen Stop-Button ein, während die Antwort streamt?
Mach aus dem Senden-Button einen Stop-Button, solange die Antwort streamt. Prompt Input nimmt einen status mit idle, submitting oder streaming entgegen. Bei submitting zeigt der Button einen Spinner, bei streaming wird er zu einem Stop-Quadrat, das onStop aufruft. Senden ist gesperrt, bis der Status wieder idle ist, Tippen aber nicht. So kann jemand die nächste Frage schreiben, während die Antwort noch streamt.
Eine Stolperfalle liegt auf dem Server. Bricht der fetch ab, schließt sich die Verbindung. Der Modellaufruf in deinem Route Handler kann aber weiterlaufen und Tokens verbrauchen, die du bezahlst, wenn er nicht ebenfalls auf den Abbruch hört. Gib request.signal aus dem Request des Route Handlers an das SDK deines Modells oder an deinen Upstream-fetch weiter. Dann beendet Stop die Generierung und nicht nur den Download.
Setz stopShortcut="escape", dann stoppt auch Escape die Antwort. Eine einzelne Taste wie Escape wirkt nur, solange der Fokus im Composer liegt. Ein Dialog auf der Seite behält also sein eigenes Escape, und ein offenes Menü oder eine offene Modellliste schließt sich zuerst, bevor die Antwort stoppt.
Zwei weitere Details:
- Das Feld leert sich sofort. Text und Dateien verschwinden aus dem Composer, sobald du sendest, und der Fokus bleibt im Feld. Gibt
onSubmitein Promise zurück, das abgelehnt wird, kommen Text und Dateien zurück (außer die Person hat schon etwas Neues getippt), und eine Fehlermeldung wird angesagt. - ArrowUp bearbeitet die letzte Nachricht. Mit
onEditLastruft ArrowUp in einem leeren Feld diese Funktion auf. So lässt sich ein Tippfehler im gerade gesendeten Prompt korrigieren.
Wie passen Diktat und Spracheingabe in den Composer?
Übergib voice, und das Mikrofon von Voice Input erscheint in der Toolbar. Während der Aufnahme nimmt eine Pill mit Live-Wellenform ihren Platz ein. Die Wörter landen dort, wo der Cursor stand, und ersetzen eine Auswahl. So kann jemand mitten in einen getippten Prompt hinein diktieren. Fertige Wörter wandern in den Text. Wörter, bei denen sich der Browser noch nicht sicher ist, bleiben abgeblendet in der Pill. Abbrechen stellt den Text genau so wieder her, wie er war.
Die Live-Wörter kommen aus der Web Speech API, die MDN mit eingeschränkter Verfügbarkeit (opens in a new tab) führt, und in Chrome geht der Ton zur Erkennung an einen Webdienst. Wo die API fehlt, wird der Ton trotzdem aufgenommen, und das Transkript bleibt leer. Willst du auf deinem Server transkribieren oder Sprachnachrichten verschicken, übergib zusätzlich onVoice. Dann bekommst du stattdessen { blob, mimeType, durationMs, transcript }. Sprache, Länge und Push-to-Talk steuerst du über voiceInputProps={{ lang: "en-US", maxDuration: 60, mode: "hold" }} (der Standardwert für maxDuration ist 120 Sekunden). Fallbacks und Wellenform erklärt der Guide zum React Speech to Text Input.
Wie bleibt ein KI-Chat-Eingabefeld auf dem Smartphone bedienbar?
Halte die Schrift bei 16px, lass die Tastatur nach dem Senden offen und heb das Feld über die Tastatur. Diese Bugs zeigen sich nur auf einem echten Smartphone, deshalb kümmert sich Prompt Input darum:
- Kein Zoom beim Fokus. iOS Safari zoomt in Eingabefelder mit Schrift unter 16px. Auf Smartphones ist die Schrift in jeder
size16px groß. Die Größensmundmdgehen erst ab 768px auf 14 und 15px herunter. - Die Tastatur bleibt offen. Ein Tipp auf Senden würde normalerweise den Fokus von der Textarea nehmen und die Tastatur schließen. Der Button ruft bei pointerdown
preventDefaultauf, solange das Feld den Fokus hat. So bleibt die Tastatur für die nächste Nachricht offen. - Über der Tastatur. Mit
position="fixed"liest der Composer den Visual Viewport überuseKeyboardInsetund hebt sich per Motion Value um die verdeckte Höhe an, ohne Re-Render pro Frame. Er schreibt seine Höhe in--prompt-input-offsetauf<html>, damit dein Chatverlauf seinen Inhalt per Padding darüber halten kann. Alle Details stehen im Beitrag iOS-Safari-Tastatur und position: fixed. - Platz für den Text. Ist der Composer schmaler als 512px, wandern die Tool-Chips als Checkbox-Zeilen ins +-Menü (auf Smartphones ein Bottom Sheet), und nur aktive Tools bleiben sichtbar. Die Prüfung misst über einen
ResizeObserverdie Breite des Composers selbst, nicht die des Viewports. Ein Composer in einem schmalen Seitenpanel auf dem Desktop wird also genauso behandelt. Der Model Picker öffnet sich unter 768px als Bottom Sheet mit 64px hohen Zeilen. - Erst wachsen, dann scrollen. Das Feld wächst bis
maxRows(standardmäßig 8), mit CSSfield-sizing: content, wo der Browser es unterstützt, und sonst mit einer Messung pro Änderung.
$ npx wingo-ui@latest add prompt-inputWohin gehören Prompt-Vorschläge als Chips?
Im leeren Chat über dem Composer und nach einer Antwort als Follow-ups. Prompt-Vorschläge als Chips sparen auf dem Smartphone Tipparbeit und zeigen, was der Assistent kann. Suggestion Chips rendert sie als Pills, Karten oder Zeilen (variant mit chip, card oder list) und merkt sich keine Auswahl: onSelect übergibt dir den Vorschlag, und du entscheidest, ob er sofort gesendet wird oder das Feld zum Bearbeiten füllt.
Das Feld zu füllen passt zu Prompts, die noch ein Detail brauchen, etwa einen Kundennamen: Gib dem Vorschlag einen prompt, der genau dort endet, wo die Person weitertippt. Unter 768px liegen die Pills in einer wischbaren Reihe, die bis an die Bildschirmränder reicht, mit 44px großen Touch-Zielen. Die ganze Gruppe ist ein einziger Tab-Stopp, innerhalb der Gruppe navigierst du mit den Pfeiltasten. Übergib disabled, während eine Antwort streamt: Auswählen ist dann gesperrt, aber der Fokus bleibt, wo er war.
$ npx wingo-ui@latest add suggestion-chipsWie sieht ein kompletter React AI Prompt Input aus?
Ein vollständiger Composer für den Next.js App Router: ein Model Picker, ein Tool, Vorschlags-Chips, die das Feld füllen, Upload-Fortschritt und ein Stop, bei dem die Teilantwort erhalten bleibt.
message enthält { text, attachments, model, tools }, mit getrimmtem Text und nur fertigen (oder lokalen) Anhängen. tokenLimit blendet ab 80% von 8.000 geschätzten Tokens einen Zähler ein und sperrt das Senden, sobald das Limit überschritten ist. Die Schätzung stammt von estimateTokens aus lib/ai-chat: ein Token pro CJK-Zeichen und eins pro vier andere Zeichen. Das reicht für eine Warnung, ersetzt aber nicht den Tokenizer deines Modells auf dem Server. Wie du den Chatverlauf rund um diesen Composer baust, mit Auto-Scroll und streamendem Markdown, zeigt der Guide zur React AI Chat UI.
Wann ist ein anderer Prompt Input die bessere Wahl?
Wenn dein Stack die Entscheidung schon vorgibt. Läuft deine App auf dem AI SDK, ist AI Elements (opens in a new tab) von Vercel kostenlos, unter Apache 2.0 lizenziert und auf shadcn/ui gebaut. Sein Prompt Input besteht aus Teilen, die du selbst zusammensetzt (PromptInputTextarea, PromptInputTools, PromptInputSubmit). Laut README (Stand Oktober 2026) wird das Projekt Ende November 2026 eingestellt und durch AI Elements von shadcn/ui (opens in a new tab) ersetzt. Neue Projekte sollten diesen Umstieg also einplanen. Unser Vergleich der AI-Elements-Alternativen stellt die anderen Kits vor. Nutzt du MUI, beherrscht dessen X Chat Composer (opens in a new tab) Enter und IME-Komposition und ist Stand Oktober 2026 als Preview gekennzeichnet. Der Composer von Wingo UI passt, wenn du eine einzige Komponente willst, bei der Smartphone-Verhalten, Uploads und Screenreader-Ansagen schon erledigt sind, und du den Transport selbst in der Hand behältst (ob fetch, SSE oder WebSocket in onSubmit).
Was solltest du als Nächstes ausprobieren?
Probier submitShortcut, position und voice im Playground des Prompt Input aus und installier ihn dann mit npx wingo-ui@latest add prompt-input. Er und die anderen Komponenten hier gehören zu Wingo UI Pro. Wie du einen Coding-Agenten mit diesen Komponenten verbindest, zeigt der MCP-Guide für Komponentenbibliotheken. Weitere Beiträge findest du unter UI mit KI-Agenten bauen.
Häufige Fragen
Wie sendet Enter in einer React-Textarea und Shift+Enter fügt eine neue Zeile ein?
Nutz onKeyDown: Ist die Taste Enter ohne Shift, ruf preventDefault auf und sende ab, sonst fügt der Browser die Zeile selbst ein. Kehr vorher sofort zurück, wenn event.nativeEvent.isComposing true ist oder keyCode 229 lautet, damit ein Enter, das eine IME-Umwandlung bestätigt, nie etwas sendet.
Soll Enter auf dem Smartphone die Nachricht senden?
Nein. Touch-Tastaturen haben kein Shift+Enter. Sendet Enter dort, kann niemand eine zweite Zeile schreiben. Lass Enter auf Touch-Geräten (pointer: coarse) eine neue Zeile einfügen, überlass das Senden dem Senden-Button und setz enterkeyhint auf enter.
Wie zeige ich den Upload-Fortschritt für Anhänge im Prompt Input?
fetch meldet keinen Upload-Fortschritt. Lade deshalb mit XMLHttpRequest hoch, lies xhr.upload.onprogress aus und gib den Anteil an den Composer weiter. Brich die Anfrage ab, wenn jemand die Datei entfernt, und biete Retry an, wenn sie scheitert.
Wie baue ich in React einen Stop-Button in einen KI-Chat-Composer?
Verwalte neben dem Composer einen Status mit idle, submitting oder streaming, zeig Stop, solange gestreamt wird, und brich die Anfrage beim Klick mit einem AbortController ab. Die Teilantwort bleibt im Verlauf, und der Status geht zurück auf idle.
Ist der Prompt Input von Wingo UI kostenlos?
Nein. Prompt Input, Suggestion Chips, Voice Input und Model Picker gehören zu Wingo UI Pro. Pro kostet $8 im Monat, $80 im Jahr oder einmalig $150 (Stand Oktober 2026), und zu den 75 kostenlosen Items gehören Textarea, Button und Tooltip, auf denen er aufbaut.
- KI-Chat
- Prompt Input
- React
- Mobile UI
- Barrierefreiheit


