# πŸŽ™οΈ expo-speech-recognition [![NPM Version](https://img.shields.io/npm/v/expo-speech-recognition)](https://www.npmjs.com/package/expo-speech-recognition) expo-speech-recognition implements the iOS [`SFSpeechRecognizer`](https://developer.apple.com/documentation/speech/sfspeechrecognizer), Android [`SpeechRecognizer`](https://developer.android.com/reference/android/speech/SpeechRecognizer) and Web [`SpeechRecognition`](https://wicg.github.io/speech-api/) for React Native projects with the goal of code reuse across web and mobile. ![expo-speech-recognition preview](./images/preview.gif) ## Table of Contents - [Installation](#installation) - [Usage](#usage) - [Using Hooks](#using-hooks) - [Permissions](#permissions) - [Direct module API](#direct-module-api) - [Speech Recognition Events](#speech-recognition-events) - [Handling Errors](#handling-errors) - [Advanced error handling](#advanced-error-handling) - [Persisting Audio Recordings](#persisting-audio-recordings) - [Transcribing audio files](#transcribing-audio-files) - [Supported input audio formats](#supported-input-audio-formats) - [File transcription example](#file-transcription-example) - [Volume metering](#volume-metering) - [Volume metering example](#volume-metering-example) - [Polyfilling the Web SpeechRecognition API](#polyfilling-the-web-speechrecognition-api) - [Muting the beep sound on Android](#muting-the-beep-sound-on-android) - [Improving accuracy of single-word prompts](#improving-accuracy-of-single-word-prompts) - [Language Detection](#language-detection) - [Platform Compatibility Table](#platform-compatibility-table) - [Common Troubleshooting issues](#common-troubleshooting-issues) - [Android issues](#android-issues) - [iOS issues](#ios-issues) - [API Methods](#api-methods) - [start()](#startoptions-speechrecognitionoptions-void) - [stop()](#stop-void) - [abort()](#abort-void) - [requestPermissionsAsync()](#requestpermissionsasync) - [requestMicrophonePermissionsAsync()](#requestmicrophonepermissionsasync) - [requestSpeechRecognizerPermissionsAsync()](#requestspeechrecognizerpermissionsasync) - [getPermissionsAsync()](#getpermissionsasync) - [getMicrophonePermissionsAsync()](#getmicrophonepermissionsasync) - [getSpeechRecognizerPermissionsAsync()](#getspeechrecognizerpermissionsasync) - [getStateAsync()](#getstateasync-promisespeechrecognitionstate) - [getSupportedLocales()](#getsupportedlocales) - [getSpeechRecognitionServices()](#getspeechrecognitionservices-string-android-only) - [getDefaultRecognitionService()](#getdefaultrecognitionservice--packagename-string--android-only) - [getAssistantService()](#getassistantservice--packagename-string--android-only) - [isRecognitionAvailable()](#isrecognitionavailable-boolean) - [supportsOnDeviceRecognition()](#supportsondevicerecognition-boolean) - [supportsRecording()](#supportsrecording-boolean) - [androidTriggerOfflineModelDownload()](#androidtriggerofflinemodeldownload) - [setCategoryIOS()](#setcategoryios-void-ios-only) - [getAudioSessionCategoryAndOptionsIOS()](#getaudiosessioncategoryandoptionsios-ios-only) - [setAudioSessionActiveIOS()](#setaudiosessionactiveiosvalue-boolean-options--notifyothersondeactivation-boolean--void) - [System Overview Diagrams](#system-overview-diagrams) - [Overall Architecture](#overall-architecture) - [Event Flow](#event-flow) ## Installation 1. Install the package ``` npm install expo-speech-recognition # Or for older SDKs: npm install expo-speech-recognition@^57.0.0 # Expo SDK 57 npm install expo-speech-recognition@^56.0.0 # Expo SDK 56 npm install expo-speech-recognition@sdk-54 npm install expo-speech-recognition@sdk-53 ``` 2. Configure the config plugin. > The config plugin updates the Android App Manifest to include package visibility filtering for `com.google.android.googlequicksearchbox` (Google's Speech Recognition) along with the required permissions for Android and iOS. To configure `androidSpeechServicePackages`, add additional speech service packages here that aren't listed under the `forceQueryable` section when running the command: `adb shell dumpsys package queries`. ```js // app.json { "expo": { "plugins": [ "plugin-one", "plugin-two", // no config (v0.2.22+) "expo-speech-recognition", // or with config [ "expo-speech-recognition", { "microphonePermission": "Allow $(PRODUCT_NAME) to use the microphone.", "speechRecognitionPermission": "Allow $(PRODUCT_NAME) to use speech recognition.", "androidSpeechServicePackages": ["com.google.android.googlequicksearchbox"] } ] // rest of your plugins ] } } ```
Important Note for Expo Beginners (click to expand) ### Important Note for Expo Beginners If you've just created a new Expo project or have only been using the Expo SDK so far (without `ios/` and `android/` directories), you'll need to create a development build. Don't worryβ€”this won't drastically change your workflow. For a better understanding of the differences between the React Native and Expo folder structures, check out this [video at the relevant timestamp](https://youtu.be/7J8LRpja9_o?t=611). After completing **Step 2: Configure the config plugin**, follow these steps: - To (re)-generate the native Android project, run: ```bash npx expo run:android ``` - To (re)-generate the native iOS project, run: ```bash npx expo run:ios ``` For more details, see [Expo's prebuild documentation](https://docs.expo.dev/workflow/prebuild/#usage-with-expo-cli-run-commands). ### Best Practices for Version Control It's recommended by the [Expo team](https://youtu.be/7J8LRpja9_o?t=611) to add the `ios/` and `android/` directories to your `.gitignore` file and rely on **Continuous Native Generation**. This approach has its own benefits, as outlined in the [Expo documentation](https://docs.expo.dev/workflow/continuous-native-generation/). Now, you're ready to move on to the [Usage](#usage) section.
## Usage ### Using Hooks Using hooks is the easiest way to get started. The `useSpeechRecognitionEvent` hook allows you to register native event listeners. ```tsx import { ExpoSpeechRecognitionModule, useSpeechRecognitionEvent, } from "expo-speech-recognition"; function App() { const [recognizing, setRecognizing] = useState(false); const [transcript, setTranscript] = useState(""); useSpeechRecognitionEvent("start", () => setRecognizing(true)); useSpeechRecognitionEvent("end", () => setRecognizing(false)); useSpeechRecognitionEvent("result", (event) => { setTranscript(event.results[0]?.transcript); }); useSpeechRecognitionEvent("error", (event) => { console.log("error code:", event.error, "error message:", event.message); }); const handleStart = async () => { const result = await ExpoSpeechRecognitionModule.requestPermissionsAsync(); if (!result.granted) { console.warn("Permissions not granted", result); return; } // Start speech recognition ExpoSpeechRecognitionModule.start({ lang: "en-US", interimResults: true, continuous: false, }); }; return ( {!recognizing ? (