react-speech-recognition
A React hook that turns microphone speech into a live text transcript using the Web Speech API.
Repository Health
Technical Analysis
react-speech-recognition exposes a single hook, useSpeechRecognition, that gives any React component a live transcript of what’s being said into the user’s microphone. A companion SpeechRecognition object provides imperative control over the browser’s microphone — starting, stopping, and aborting listening — decoupled from any individual component, so multiple components can independently subscribe to the same shared recognition session without prop drilling.
Under the hood it wraps the browser-native Web Speech API and gracefully degrades on browsers that don’t support it, with first-class support for swapping in a cloud speech-to-text polyfill (e.g. Azure Cognitive Services, Speechly) so voice features work consistently across browsers rather than only on Chrome desktop. It also ships a voice-command system: developers can register phrases or regular expressions with callbacks, including fuzzy matching via Dice-coefficient string similarity for commands that are easy to mishear or mispronounce.
The library has been maintained since 2016, is used across a large number of production React apps (246k+ weekly npm downloads), and requires React 16.8+ for hooks support.
What You Get
- The
useSpeechRecognitionhook, returning a livetranscript, splitinterimTranscript/finalTranscriptstate,listeningstatus, andisMicrophoneAvailable - A
SpeechRecognitionobject for imperative control —startListening,stopListening,abortListening,applyPolyfill,removePolyfill— shared globally across all components - A voice-command system supporting string/RegExp commands, named parameters, interim-result matching, and fuzzy (Dice-coefficient) matching for commands prone to mishearing
- Graceful feature detection via
browserSupportsSpeechRecognitionandbrowserSupportsContinuousListening, so apps can render fallback UI where speech isn’t supported - A pluggable polyfill layer (documented integrations for Azure Cognitive Services and Speechly) so voice features behave consistently across browsers, not just Chrome
Common Use Cases
- Voice-driven dictation fields where spoken words are transcribed directly into a text input
- Hands-free voice command interfaces (e.g. “scroll down”, “open menu”) using the fuzzy or exact command matcher
- Accessibility features that let users interact with a web app by speaking instead of typing
- Push-to-talk UI patterns built on top of a cloud speech-to-text polyfill for cross-browser consistency
Under The Hood
Architecture
The library separates concerns across three layers: SpeechRecognition.js exposes both the useSpeechRecognition hook and a static SpeechRecognition namespace; RecognitionManager.js is a plain (non-React) class holding the actual recognition session, transcript state, and a subscriber registry keyed by hook-instance id; and NativeSpeechRecognition.js resolves whichever vendor-prefixed SpeechRecognition constructor the browser exposes (or lets applyPolyfill swap in a different one entirely). Each mounted useSpeechRecognition call subscribes to the single shared RecognitionManager instance via useEffect, translating its imperative emitListeningChange/emitTranscriptChange callbacks into React state through a small reducer (transcriptReducer in reducers.js) and a command-matching pass (matchCommands, testMatch, testFuzzyMatch in the hook) built on a regex compiler in utils.js adapted from Backbone.Router. Because recognition state lives outside React (in RecognitionManager, not component state), multiple hook consumers share one microphone session without prop drilling, and the core abstraction that would be costly to change is the pub/sub subscriber map — any change there ripples through every field the hook returns.
Tech Stack
The package is plain JavaScript (no TypeScript; type definitions are community-maintained in DefinitelyTyped) with a peer dependency on React 16.8+ for hooks, and a single runtime dependency, lodash.debounce, used to throttle transcript updates on Android where the Web Speech API fires results more aggressively. It’s built with bunchee, a zero-config bundler that produces dual ESM (dist/index.js) and CJS (dist/index.cjs) output declared via package.json exports. Tests run on Vitest with jsdom and @testing-library/react-hooks, plus a vendored corti.js Web Speech API mock in tests/vendor/. Linting and formatting go through Biome, and the project uses Yarn 4 (Berry) workspaces with Corepack.
Code Quality
Core modules are tested directly — SpeechRecognition.test.js is over 1,000 lines covering hook behavior, command matching, and polyfill switching, plus dedicated tests for Android-specific debounce behavior (android.test.js) and browser detection (isAndroid.test.js). Coverage is tracked in CI via Vitest’s --coverage flag and reported to Coveralls. Error handling is deliberate rather than exhaustive: RecognitionManager.startListening catches microphone-start failures and explicitly comments on why DOMException cases are swallowed (a redundant start is expected and safe), while genuine permission failures flip isMicrophoneAvailable. Naming is consistent (camelCase, action-creator functions in actions.js), but there is no static type system beyond community type definitions, so type safety is not enforced at build time. GitHub Actions runs lint, build, and test-with-coverage on every push and PR.
API Design
The public surface is intentionally narrow — one hook and one imperative namespace — which keeps the getting-started cost low: a component needs only useSpeechRecognition() and a startListening call to work. Sensible defaults (transcribing: true, clearTranscriptOnListen: true) mean most consumers never touch configuration. The command-matching system is a more ambitious DX feature than a typical thin wrapper: it supports string, array, and RegExp commands, named parameters extracted from spoken phrases, and fuzzy matching with a configurable similarity threshold for commands that are easy to mishear — a feature not found in comparable speech-hook libraries. The tradeoff is that some behavior (continuous listening, polyfill swapping) is controlled through global static methods on SpeechRecognition rather than hook arguments, which is a reasonable design for a shared browser resource like the microphone but does mean state is not fully colocated with the component that reads it.
Used by 2 apps in this directory
AnythingLLM
Developer Tools · Automation · AI Assistants
The all-in-one AI platform for private document chat, no-code agents, and local LLMs with zero setup friction.
LibreChat
Developer Tools · AI Assistants
Unite every major AI model in one self-hosted chat platform with agents, code execution, MCP tools, and enterprise authentication.