# React Speech to Text: Mikrofon, Waveform, Fallback

> React Speech to Text bauen: ein Mikrofon, das zur Aufnahme-Pill mit Live-Waveform wird, was die Web Speech API je Browser kann und wann der Server übernimmt.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 16. Okt. 2026
- Kategorie: [UI mit KI-Agenten bauen](https://wingo-ui.com/de/blog/category/ai-agents)
- Lesezeit: 9 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-speech-to-text-input
- Englische Fassung: https://wingo-ui.com/blog/react-speech-to-text-input

## Kurz gesagt

Ein React Speech to Text Input nimmt mit MediaRecorder auf und zeigt Live-Wörter aus der Web Speech API, wo der Browser sie anbietet: Chrome und Safari haben sie, Firefox standardmäßig nicht, und Chrome schickt das Audio zur Erkennung an einen Server. Behandle das Browser-Transkript als Vorschau, behalte den Audio-Blob und schick ihn an einen Transkriptions-Endpoint, wenn der Browser keine Wörter liefert oder du überall dasselbe Ergebnis brauchst.

Ein React Speech to Text Input sieht aus wie ein einziger Button: aufs Mikrofon tippen, sprechen, Text bekommen. Damit er im Alltag hält, braucht es mehr. Der Browser muss im richtigen Moment nach dem Mikrofon fragen. Die Person muss sehen, dass zugehört wird. Die Wörter sollen beim Sprechen erscheinen. Und das Ganze muss auch in Firefox funktionieren, in einer Home-Screen-App auf iOS und wenn der Erkennungsdienst ausfällt. Dieser Guide geht jeden dieser Punkte durch, mit dem Code von [Voice Input](https://wingo-ui.com/components/voice-input) aus Wingo UI: einem Mikrofon, das zu einer Aufnahme-Pill mit Live-Waveform wächst. Wir bauen diese Bibliothek, sind also nicht neutral; Fakten zu den Browsern verlinken auf ihre Quellen.

```bash
npx wingo-ui@latest add voice-input
```

## Wie sieht ein guter Speech to Text Input aus?

Im Ruhezustand ein Mikrofon-Button, beim Sprechen eine Aufnahme-Pill, am Ende der Text. Voice Input hat drei Formen hinter `variant`:

- **`button`** (Standard): ein rundes Mikrofon für Composer und Toolbars. Tippst du darauf, wächst es mit einer Feder-Animation zu einer Pill mit Abbrechen, pulsierendem rotem Punkt, Waveform, Timer und Senden. Danach schrumpft es wieder zum Mikrofon.
- **`bar`**: eine Leiste über die volle Breite, die immer sichtbar ist, das Layout aus Messaging-Apps, mit dem Transkript darüber.
- **`orb`**: ein Sprachmodus auf Basis des [Matrix Orb](https://wingo-ui.com/components/matrix-orb), der zuhört, nachdenkt, während dein Promise läuft, und aufblüht, während die App antwortet.

Die Live-Wörter stehen über der Pill, vier Zeilen hoch. Wörter, bei denen sich die Erkennung noch nicht sicher ist, erscheinen gedämpft. Der Aufnahmepunkt bleibt rot, egal welchen `tone` du wählst, denn Rot heißt Aufnahme.

> Live-Demo (Voice Input): Tipp aufs Mikrofon und erlaube den Zugriff: Es wächst zu einer Pill mit einer Waveform, die deiner Stimme folgt. Sprich, dann drück Senden oder Escape zum Abbrechen. Probier sie unter [Voice Input](https://wingo-ui.com/components/voice-input) aus und installier sie mit `npx wingo-ui@latest add voice-input`.

## Wie funktioniert die Web Speech API in React, und wo hakt es?

Ein Web Speech API React Hook ist kurz. Er kapselt `SpeechRecognition`: Recognizer anlegen, `continuous` und `interimResults` setzen, `onresult` lesen, beim Unmount stoppen. Ärger machen die Browser und Modi drumherum:

| Browser | SpeechRecognition | Hinweise |
|---|---|---|
| Chrome Desktop | Ja, mit `webkit`-Präfix seit 33; MDN führt den Namen ohne Präfix ab 139 | Audio geht zur Erkennung an einen Server |
| Safari 14.1+ (macOS, iOS) | Ja, `webkitSpeechRecognition` | Siri oder Diktierfunktion muss aktiviert sein; nicht in Home-Screen-Web-Apps |
| Firefox | Nein | Standardmäßig aus; Can I use führt die API als deaktiviert |
| Edge | Unklar | Die Daten von MDN spiegeln Chrome; Can I use führt die API als nicht unterstützt, weil offenbar keine Events feuern. Teste es selbst |

Quellen, geprüft im Oktober 2026: [MDN SpeechRecognition](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition), [MDN browser compat data](https://github.com/mdn/browser-compat-data/blob/main/api/SpeechRecognition.json), [Can I use](https://caniuse.com/speech-recognition) und [WebKit bug 225298](https://bugs.webkit.org/show_bug.cgi?id=225298). Dort antwortet WebKit, dass die API in SafariViewController und in Home-Screen-Web-Apps vorerst nicht verfügbar ist.

Drei weitere Grenzen bestimmen das Design:

1. **Sie ist standardmäßig nicht privat.** Laut [MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition) schickt Chrome dein Audio zur Verarbeitung an einen Webdienst, offline geht also nichts. Chrome 139 hat auf dem Desktop eine experimentelle Option `processLocally` eingeführt, dazu `SpeechRecognition.available()` / `install()` für Sprachpakete auf dem Gerät; Android und Safari haben sie laut den Daten von MDN nicht.
2. **Chrome hört von selbst auf.** Ein kontinuierlicher Lauf endet nach einer Pause, `onend` feuert und das Transkript wächst nicht mehr. Voice Input startet den Recognizer in `onend` neu, solange du noch aufnimmst, und fügt den fertigen Text jedes Laufs zusammen.
3. **Sie braucht eine Nutzeraktion und HTTPS.** Browser öffnen das Mikrofon nur nach einem Tippen oder Tastendruck, und unter reinem HTTP gibt es `navigator.mediaDevices` nicht. Voice Input fragt erst nach dem ersten Tippen nach der Berechtigung, nie beim Laden der Seite, und zeigt „Voice input isn't available in this browser.“, wenn es gar nicht aufnehmen kann.

Behandle das Browser-Transkript also als schnelle Vorschau. Die Quelle der Wahrheit ist das Audio.

## Wie bekomme ich in React den Audio-Blob und das Transkript?

Wenn die Person Senden drückt, bekommt `onRecordingComplete` das Objekt `{ blob, mimeType, durationMs, transcript }`. Das Audio kommt von `MediaRecorder`, der den ersten Typ wählt, den der Browser aufnehmen kann: `audio/webm;codecs=opus` in Chrome, `audio/mp4` in Safari-Versionen ohne WebM-Aufnahme. Das Transkript kommt vom Recognizer des Browsers oder aus dem Prop `transcript`, wenn du Wörter aus deinem eigenen Dienst streamst. Jede Quelle lässt sich an- und abschalten:

- `audio={false}`: keine Aufnahme, nur Wörter und Länge. Gut für reines Diktieren.
- `transcribe="none"`: kein Recognizer im Browser, nur Transkription auf dem Server.
- `lang="en-US"`: die Sprache der Erkennung. Bleibt es leer, gilt der Prop `locale`, dann die Locale des `UIProvider`, dann `<html lang>` der Seite und zuletzt die Sprache des Browsers.

Gibst du ein Promise zurück, zeigt die Pill einen Spinner, bis es abgeschlossen ist. Wird es abgelehnt, erscheint eine Fehlerzeile mit einem Button für einen neuen Versuch, der dieselbe Aufnahme noch einmal sendet.

## Wie bekommt jedes Textfeld in React einen Diktier-Button?

So sieht der Diktier-Button aus, den React-Formulare meistens brauchen: nur Wörter, angehängt an den Text, der schon im Feld steht.

```tsx
"use client"

import { useState } from "react"
import { Textarea } from "@/components/ui/textarea"
import { VoiceInput } from "@/components/ui/voice-input"

export function DictatedNote() {
  const [note, setNote] = useState("")

  return (
    <div className="flex flex-col gap-2">
      <Textarea label="Notiz" value={note} onValueChange={setNote} minRows={4} />
      <div className="flex justify-end">
        <VoiceInput
          audio={false}
          lang="en-US"
          maxDuration={60}
          onRecordingComplete={({ transcript }) => {
            if (transcript) setNote((text) => [text, transcript].filter(Boolean).join(" "))
          }}
        />
      </div>
    </div>
  )
}
```

`onTranscript(text, { final })` feuert, während die Person spricht, falls die Wörter live im Feld landen sollen. In Firefox nimmt diese Version nichts Brauchbares auf, denn dort gibt es keinen Recognizer und kein Audio. Der nächste Abschnitt behebt das.

## Wie nutze ich Transkription auf dem Server als Fallback?

Behalte das Audio, und wenn der Browser dir keine Wörter liefert, schick den Blob an deinen eigenen Endpoint. Der Client bleibt in jedem Browser gleich:

```tsx
"use client"

import { VoiceInput, type VoiceInputResult } from "@/components/ui/voice-input"

async function transcribeOnServer(result: VoiceInputResult) {
  if (!result.blob) return ""
  // chrome nimmt audio/webm auf, safari unter umständen audio/mp4: die datei danach benennen
  const ext = result.mimeType?.includes("mp4") ? "mp4" : "webm"
  const body = new FormData()
  body.append("audio", result.blob, `dictation.${ext}`)
  const response = await fetch("/api/transcribe", { method: "POST", body })
  if (!response.ok) throw new Error("Transkription fehlgeschlagen")
  const { text } = (await response.json()) as { text: string }
  return text
}

export function Dictation({ onText }: { onText: (text: string) => void }) {
  return (
    <VoiceInput
      lang="en-US"
      maxDuration={120}
      onRecordingComplete={async (result) => {
        // die vorschau des browsers, wenn es eine gibt, sonst der server
        onText(result.transcript || (await transcribeOnServer(result)))
      }}
    />
  )
}
```

Der Route Handler hält den API-Key auf dem Server. Dieser hier nutzt den Transkriptions-Endpoint von OpenAI; jeder Anbieter, der eine Datei annimmt, funktioniert genauso.

```ts
// app/api/transcribe/route.ts
import OpenAI from "openai"

const openai = new OpenAI()

export async function POST(request: Request) {
  const form = await request.formData()
  const audio = form.get("audio")
  if (!(audio instanceof File)) return Response.json({ error: "Keine Audiodatei" }, { status: 400 })

  const transcription = await openai.audio.transcriptions.create({
    file: audio,
    model: "gpt-transcribe",
  })
  return Response.json({ text: transcription.text })
}
```

Stand Oktober 2026 nennt der [Speech-to-Text-Guide von OpenAI](https://developers.openai.com/api/docs/guides/speech-to-text) `gpt-transcribe` für aufgenommene Sprache, ein Upload-Limit von 25 MB und unter den angenommenen Formaten `webm`, `mp4`, `m4a`, `wav` und `mp3`. Die Aufnahmen beider Browser lassen sich also unverändert hochladen. `maxDuration` (standardmäßig 120 Sekunden) verhindert, dass eine vergessene Aufnahme größer wird, als du hochladen willst.

Willst du in jedem Browser dieselben Wörter, setz `transcribe="none"` und nutze immer den Server. Die Pill zeigt dann an, dass transkribiert wird, solange dein Promise läuft.

## Wie passt Spracheingabe in eine Chat-UI?

In einer Voice Input Chat UI sitzt das Mikrofon im Composer. Der [Prompt Input](https://wingo-ui.com/components/prompt-input) erledigt das mit einem Prop: `voice` fügt das Mikrofon in die Toolbar ein, während der Aufnahme übernimmt die Pill die Toolbar, und die Wörter landen an der Cursorposition und ersetzen markierten Text. Abbrechen stellt den ursprünglichen Text wieder her.

```tsx
"use client"

import { PromptInput } from "@/components/ui/prompt-input"

export function Composer({ send }: { send: (text: string) => Promise<void> }) {
  return (
    <PromptInput
      voice
      voiceInputProps={{ lang: "en-US", maxDuration: 60, mode: "hold" }}
      onSubmit={({ text }) => send(text)}
    />
  )
}
```

Gibst du zusätzlich `onVoice` mit, sendet das Mikrofon Sprachnachrichten, `{ blob, mimeType, durationMs, transcript }`, statt zu diktieren. Der [Guide zum React Prompt Input](https://wingo-ui.com/de/blog/react-prompt-input) behandelt den Rest des Composers, der [Guide zur React AI Chat UI](https://wingo-ui.com/de/blog/react-ai-chat-ui) den Thread drumherum.

> Live-Demo (Prompt Input): Tipp aufs Mikrofon in der Toolbar und sprich: Die Pill ersetzt die Toolbar und die Wörter landen an der Cursorposition. Drück Enter zum Senden. Probier sie unter [Prompt Input](https://wingo-ui.com/components/prompt-input) aus und installier sie mit `npx wingo-ui@latest add prompt-input`.

## Wie spiele ich eine Sprachnachricht mit ihrer Waveform ab?

Eine React Voice Recorder Waveform hat zwei Aufgaben: Während der Aufnahme bewegt sie sich live, nach dem Senden steht sie still. Während der Aufnahme liest Voice Input das Mikrofon über einen Analyser und schreibt die Transforms der Balken in einer `requestAnimationFrame`-Schleife direkt ins DOM, nie über React-State, und pausiert sie außerhalb des sichtbaren Bereichs. Dieselben Balken gibt es als `VoiceWaveform` für eigene Layouts; mit `values` zeigt die Komponente die feste Waveform einer aufgenommenen Notiz.

Für die Wiedergabe hat der [Audio Player](https://wingo-ui.com/components/audio-player) die Variante `compact`, gedacht für Chat-Bubbles: Play, Balken zum Scrubben, Zeit und ein Chip für die Geschwindigkeit. Ohne `peaks` dekodiert er die Datei mit Web Audio, sobald er sichtbar ist. So bekommt auch eine Object URL aus dem Blob ihre echte Waveform.

```tsx
"use client"

import { useEffect, useState } from "react"
import { AudioPlayer } from "@/components/ui/audio-player"

export function VoiceMessage({ blob }: { blob: Blob }) {
  const [src, setSrc] = useState("")

  // eine object url pro blob, freigegeben, wenn die bubble verschwindet
  useEffect(() => {
    const url = URL.createObjectURL(blob)
    setSrc(url)
    return () => URL.revokeObjectURL(url)
  }, [blob])

  return src ? <AudioPlayer variant="compact" src={src} /> : null
}
```

Sobald der Upload fertig ist, tausch die Object URL gegen die URL der gespeicherten Datei und gib `peaks` mit, wenn dein Server sie schon berechnet hat.

> Live-Demo (Audio Player): Spiel die Sprachnachricht ab, zieh dann über die Balken zum Scrubben und tipp auf den Chip für die Geschwindigkeit. Probier sie unter [Audio Player](https://wingo-ui.com/components/audio-player) aus und installier sie mit `npx wingo-ui@latest add audio-player`.

## Wie mache ich eine React Spracheingabe barrierefrei für Tastatur, Screenreader und Smartphones?

Gib dem Mikrofon einen echten Button, kündige Zustandswechsel einmal an und mach jedes Touch-Ziel groß genug für einen Daumen. Voice Input erledigt all das:

- **Tastatur.** Mit Tab zum Mikrofon, dann Enter oder Leertaste. Während der Aufnahme springt der Fokus auf Senden, und Escape bricht ab.
- **Screenreader.** Sie sagen jeweils einmal an, dass zugehört oder transkribiert wird und wie lang die Aufnahme ist, aber nie den tickenden Timer.
- **Smartphones.** Jedes Ziel ist auf Touch-Geräten 44px groß. `mode="hold"` ergibt Push to Talk: halten, zum Senden loslassen, und wer mehr als 80px Richtung Anfang wischt, bricht beim Loslassen ab, bestätigt durch einen haptischen Tick (der [Beitrag zur Vibration API](https://wingo-ui.com/de/blog/web-vibration-api-haptic-feedback) erklärt, wo das funktioniert). Das gehaltene Mikrofon setzt `touch-action: none`, damit die Seite unter deinem Daumen nicht scrollt.
- **Fehltipper.** Eine Aufnahme, die kürzer ist als `minDuration` (standardmäßig 500 ms), wird verworfen, und ein Hinweis bittet um eine längere Nachricht.
- **Fehler.** Ein blockiertes oder fehlendes Mikrofon erklärt das Problem direkt in der Komponente und bietet einen neuen Versuch an.
- **Reduzierte Bewegung.** Aus dem Morph wird eine Überblendung, aus der Waveform eine ruhige Pegelanzeige.

## Was sind die typischen Fehler bei Speech to Text?

Die meisten Bugs kommen von der Berechtigungsabfrage, von Mikrofon-Streams, die offen bleiben, und von Tests in nur einem Browser.

- **Beim Laden der Seite nach dem Mikrofon fragen.** Eine Abfrage, um die niemand gebeten hat, wird abgelehnt, und die Ablehnung bleibt. Frag beim ersten Tippen.
- **Dem vorläufigen Text vertrauen.** Vorläufige Wörter ändern sich. Speichere das finale Transkript oder das vom Server.
- **Die Tracks vergessen.** Stopp jeden `MediaStream`-Track, jeden Audio-Kontext und jeden Recognizer bei Stopp, Abbruch und Unmount, sonst bleibt die Aufnahmeanzeige des Browsers an. Der Hook `useVoiceRecorder` erledigt das, wenn du eine eigene UI auf derselben Engine baust.
- **Eine Dateiendung für alle Browser.** Eine Safari-Aufnahme mit der Endung `.webm` verwirrt manche APIs. Benenn sie nach dem MIME-Type.
- **Zwei Mikrofone auf einer Seite.** Ein Mikrofon im Composer und eines für Sprachnotizen können beide starten. Bei Voice Input stoppt und sendet ein zweiter Recorder beim Start den ersten, sodass immer nur ein Stream das Mikrofon hält.
- **Nur in Chrome testen.** Probier vor dem Release Firefox, Safari mit ausgeschalteter Diktierfunktion und eine Installation auf dem Home-Screen.

## Wie geht es weiter?

Öffne die [Doku zu Voice Input](https://wingo-ui.com/components/voice-input), schalte im Playground `variant`, `mode` und `transcribe` um und installier die Komponente mit `npx wingo-ui@latest add voice-input`. Voice Input, Prompt Input und Audio Player gehören zu [Wingo UI Pro](https://wingo-ui.com/de/pricing). Wenn ein Coding-Agent deine Screens baut, zeigt der [Guide zum MCP-Server für Komponentenbibliotheken](https://wingo-ui.com/de/blog/component-library-mcp-guide), wie du ihn auf diese Komponenten ansetzt. Weitere Beiträge findest du unter [UI mit KI-Agenten bauen](https://wingo-ui.com/de/blog/category/ai-agents).

## Komponenten in diesem Artikel

- [Voice Input](https://wingo-ui.com/components/voice-input) (Pro): A React voice input: a mic that grows into a recording pill with a live waveform and transcript, a full-width bar, or a voice orb. Installation: `npx wingo-ui@latest add voice-input`
- [Prompt Input](https://wingo-ui.com/components/prompt-input) (Pro): A React prompt input for AI chat: a growing text box with attachments, tool chips, a model picker, dictation and a send and stop button. Installation: `npx wingo-ui@latest add prompt-input`
- [Audio Player](https://wingo-ui.com/components/audio-player) (Pro): A React audio player with a waveform you can see and scrub, as a podcast card, a chat voice message or a thin call-log bar. Installation: `npx wingo-ui@latest add audio-player`

## Häufige Fragen

### Funktioniert die Web Speech API in jedem Browser?

Nein. MDN führt SpeechRecognition als eingeschränkt verfügbar: Chrome und Safari ab 14.1 liefern sie aus (Safari mit webkit-Präfix), Firefox hat sie standardmäßig deaktiviert, und Safari bietet sie in Home-Screen-Web-Apps nicht an. Prüf SpeechRecognition und webkitSpeechRecognition per Feature Detection und halte einen Fallback bereit.

### Ist die Spracherkennung im Browser privat oder offline?

Standardmäßig nicht. Laut MDN schickt Chrome das Audio zur Erkennung an einen Webdienst, offline funktioniert sie also nicht. Chrome 139 hat auf dem Desktop eine experimentelle Option processLocally eingeführt, darauf solltest du dich aber noch nicht verlassen.

### Wie schicke ich aufgenommenes Audio aus React an eine Speech-to-Text-API?

Nimm mit MediaRecorder auf, pack den Blob in einen FormData-Body und schick ihn per POST an deinen eigenen Route Handler, der ihn mit dem API-Key auf dem Server an die Transkriptions-API weiterreicht. Wähl die Dateiendung nach dem MIME-Type des Recorders, denn Chrome nimmt audio/webm auf und Safari unter Umständen audio/mp4.

### Warum hört mein Web-Speech-API-Transkript nach einer Pause auf?

Chrome beendet einen kontinuierlichen Erkennungslauf nach einer Stille von selbst, und das Event onend feuert. Starte die Erkennung in onend neu, solange die Person noch aufnimmt, und füg den fertigen Text jedes Laufs zusammen, damit nichts verloren geht.

### Ist Voice Input von Wingo UI kostenlos?

Nein. Voice Input, Prompt Input und Audio Player gehören zu Wingo UI Pro. Stand Oktober 2026 enthalten die 75 kostenlosen Elemente Button, Tooltip, Spinner und Matrix Orb, auf denen Voice Input aufbaut.

---

Quelle: https://wingo-ui.com/de/blog/react-speech-to-text-input
