React Native Speech Recognition Library powered by Nitro Modules
npm install @gmessier/nitro-speech


> ⚠️ Work in Progress
>
> This library is under active development. (Last version is stable)
Speech recognition for React Native, powered by Nitro Modules.
- Installation
- Permissions
- Features
- Usage
- Recommended: useRecognizer Hook
- With React Navigation (important)
- Alternative: Static Recognizer
- API Reference
- Requirements
- Troubleshooting
``bash`
npm install @gmessier/nitro-speech react-native-nitro-modulesor
yarn add @gmessier/nitro-speech react-native-nitro-modulesor
bun add @gmessier/nitro-speech react-native-nitro-modules
This library works with Expo. You need to run prebuild to generate native code:
`bash`
npx expo prebuild
Note: Make sure New Arch is enabled in your Expo configuration before running prebuild.
`bash`
cd ios && pod install
No additional setup required.
The library declares the required permission in its AndroidManifest.xml (merged automatically):
`xml`
Add the following keys to your app's Info.plist:
`xml`
Both permissions are required for speech recognition to work on iOS.
| Feature | Description | iOS | Android |
|---------|-------------|-----|---------|
| Real-time transcription | Get partial results as the user speaks, enabling live UI updates | ✅ | ✅ |
| Auto-stop on silence | Automatically stops recognition after configurable inactivity period (default: 8s) | ✅ | ✅ |
| Auto-finish progress | Progress callbacks showing countdown until auto-stop | ✅ | ❌ (TODO) |
| Locale support | Configure speech recognizer for different languages | ✅ | ✅ |
| Background handling | Auto-stop when app loses focus/goes to background | ✅ | Not Safe (TODO) |
| Contextual strings | Domain-specific vocabulary for improved accuracy | ✅ | ✅ |
| Repeating word filter | Removes consecutive duplicate words from artifacts | ✅ | ✅ |
| Permission handling | Dedicated onPermissionDenied callback | ✅ | ✅ |
| Haptic feedback | Optional haptics on recording start/stop | ✅ | ✅ |
| Automatic punctuation | Adds punctuation to transcription (iOS 16+) | ✅ | Auto |
| Language model selection | Choose between web search vs free-form models | Auto | ✅ |
| Offensive word masking | Control whether offensive words are masked | Auto | ✅ |
| Formatting quality | Prefer quality vs speed in formatting | Auto | ✅ |
`typescript
import { useRecognizer } from '@gmessier/nitro-speech';
function MyComponent() {
const {
startListening,
stopListening,
addAutoFinishTime,
updateAutoFinishTime
} = useRecognizer({
onReadyForSpeech: () => {
console.log('Listening...');
},
onResult: (textBatches) => {
console.log('Result:', textBatches.join('\n'));
},
onRecordingStopped: () => {
console.log('Stopped');
},
onAutoFinishProgress: (timeLeftMs) => {
console.log('Auto-stop in:', timeLeftMs, 'ms');
},
onError: (error) => {
console.log('Error:', error);
},
onPermissionDenied: () => {
console.log('Permission denied');
},
});
return (
locale: 'en-US',
autoFinishRecognitionMs: 8000,
contextualStrings: ['custom', 'words'],
// Haptics (both platforms)
startHapticFeedbackStyle: 'medium',
stopHapticFeedbackStyle: 'light',
// iOS specific
iosAddPunctuation: true,
// Android specific
androidMaskOffensiveWords: false,
androidFormattingPreferQuality: false,
androidUseWebSearchModel: false,
})}>
);
}
`
React Navigation doesn’t unmount screens when you navigate — the screen can stay mounted in the background and come back without remounting. See: Navigation lifecycle (React Navigation).
Because of that, prefer tying recognition cleanup to focus state, not just component unmount. A simple approach is useIsFocused() and passing it into useRecognizer’s destroyDeps so recognition stops when the screen blurs. See: useIsFocused (React Navigation).
`typescript`
const isFocused = useIsFocused();
const {
// ...
} = useRecognizer(
{
// ...
},
[isFocused]
);
`typescript
import { Recognizer } from '@gmessier/nitro-speech';
// Set up callbacks
Recognizer.onReadyForSpeech = () => {
console.log('Listening...');
};
Recognizer.onResult = (textBatches) => {
console.log('Result:', textBatches.join('\n'));
};
Recognizer.onRecordingStopped = () => {
console.log('Stopped');
};
Recognizer.onAutoFinishProgress = (timeLeftMs) => {
console.log('Auto-stop in:', timeLeftMs, 'ms');
};
Recognizer.onError = (error) => {
console.log('Error:', error);
};
Recognizer.onPermissionDenied = () => {
console.log('Permission denied');
};
// Start listening
Recognizer.startListening({
locale: 'en-US',
});
// Stop listening
Recognizer.stopListening();
// Manually add time to auto finish timer
Recognizer.addAutoFinishTime(5000); // Add 5 seconds
Recognizer.addAutoFinishTime(); // Reset to original time
// Update auto finish time
Recognizer.updateAutoFinishTime(10000); // Set to 10 seconds
Recognizer.updateAutoFinishTime(10000, true); // Set to 10 seconds and refresh progress
`
The Recognizer.dispose() method is NOT SAFE and should rarely be used. Hybrid Objects in Nitro are typically managed by the JS garbage collector automatically. Only call dispose() in performance-critical scenarios where you need to eagerly destroy objects.
See: Nitro dispose() documentation
A React hook that provides lifecycle-aware access to the speech recognizer.
#### Parameters
- callbacks (object):onReadyForSpeech?: () => void
- - Called when speech recognition startsonResult?: (textBatches: string[]) => void
- - Called every time when partial result is ready (array of text batches)onRecordingStopped?: () => void
- - Called when recording stopsonAutoFinishProgress?: (timeLeftMs: number) => void
- - Called each second during auto-finish countdownonError?: (message: string) => void
- - Called when an error occursonPermissionDenied?: () => void
- - Called if microphone permission is denieddestroyDeps
- (array, optional) - Additional dependencies for the cleanup effect. When any of these change (or the component unmounts), recognition is stopped.
#### Returns
- startListening(params: SpeechToTextParams) - Start speech recognition with the given parametersstopListening()
- - Stop speech recognitionaddAutoFinishTime(additionalTimeMs?: number)
- - Add time to the auto-finish timer (or reset to original if no parameter)updateAutoFinishTime(newTimeMs: number, withRefresh?: boolean)
- - Update the auto-finish timer
Configuration object for speech recognition.
#### Common Parameters
- locale?: string - Language locale (default: "en-US")autoFinishRecognitionMs?: number
- - Auto-stop timeout in milliseconds (default: 8000)contextualStrings?: string[]
- - Array of domain-specific words for better recognitiondisableRepeatingFilter?: boolean
- - Disable filter that removes consecutive duplicate words (default: false)startHapticFeedbackStyle?: 'light' | 'medium' | 'heavy'
- - Haptic feedback style when microphone starts recording (default: null / disabled)stopHapticFeedbackStyle?: 'light' | 'medium' | 'heavy'
- - Haptic feedback style when microphone stops recording (default: null / disabled)
#### iOS-Specific Parameters
- iosAddPunctuation?: boolean - Add punctuation to results (iOS 16+, default: true)
#### Android-Specific Parameters
- androidMaskOffensiveWords?: boolean - Mask offensive words (Android 13+, default: false)androidFormattingPreferQuality?: boolean
- - Prefer quality over latency (Android 13+, default: false)androidUseWebSearchModel?: boolean
- - Use web search language model instead of free-form (default: false)androidDisableBatchHandling?: boolean
- - Disable default batch handling (may add many empty batches, default: false)
- React Native >= 0.76
- New Arch Only
- react-native-nitro-modules
If you're having issues with Android Gradle sync, try running the prebuild for the core Nitro library:
`bash``
cd android && ./gradlew :react-native-nitro-modules:preBuild
MIT
- [ ] (Android) Timer till the auto finish is called
- [ ] (Android) Cleanup when app loses the focus