v2.0.0 is a full rewrite of the native layer. The JavaScript API is mostly compatible but there are several breaking changes to be aware of.
- What changed
- Platform & environment requirements
- Dependencies
- VisionCamera v4 → v5
- Hooks — return type changes
- Frame processors — custom integrations
- TranslatorPlugin —
translatesignature changed - PhotoRecognizer — no behavioral change, but new backing
- RemoveLanguageModel — no behavioral change, but new backing
- Worklets package change
- Type exports — now public
- iOS Simulator caveat
- Quick checklist
The v1 implementation used VisionCamera's VisionCameraProxy.initFrameProcessorPlugin API along with NativeModules for photo OCR and model management. v2 replaces all of this with Nitro Modules — a JSI-based native module system that exposes TextRecognizer and Translator as first-class HybridObjects. This eliminates the old frame processor plugin registration and the separate NativeModules bridge for PhotoRecognizer / RemoveLanguageModel.
v2 raises the minimum supported versions across the board. Ensure your project meets these before upgrading:
| Requirement | Minimum version |
|---|---|
| React Native | 0.81 |
| iOS | 15.1 |
| Android Minimum SDK | 26 |
| Android Target SDK | 36 |
| react-native-vision-camera | 5.0.0 |
| react-native-worklets | 0.8.x |
| Expo (if used) | 54 |
If your project targets Android SDK < 26 or iOS < 15.1, you will need to raise those targets before upgrading. In your android/build.gradle set minSdkVersion = 26; in your iOS Podfile ensure platform :ios, '15.1' (or higher).
Firebase users: If your project includes Firebase, you must set the iOS Deployment Target to at least 16.0.
# no longer needed as a peer dependency
yarn remove react-native-worklets-coreyarn add react-native-nitro-modules react-native-vision-camera-worklets react-native-worklets| Package | Version |
|---|---|
react-native-vision-camera |
>=5.0.0 |
react-native-nitro-modules |
* |
react-native-vision-camera-worklets |
* |
react-native-worklets |
* |
react-native-worklets-core is no longer required.
v2 requires VisionCamera v5 (>=5.0.0). The most notable API change that affects this library's users directly is how pixelFormat is set — in v5 it is configured on the frame output, not on the <Camera> component.
If you are using the <Camera> component or the low-level hooks from this library you do not need to set pixelFormat yourself — the library handles it internally. However, if you build your own frame processor with useFrameOutput, you must set pixelFormat: 'rgb' on Android:
// v5 custom frame output — Android requires pixelFormat: 'rgb'
const frameOutput = useFrameOutput({
pixelFormat: 'rgb',
onFrame: (frame) => { ... },
})See the VisionCamera v5 migration guide for the full list of VisionCamera-level changes.
The return type changed from TextRecognitionPlugin to TextRecognitionHandle.
TextRecognitionHandle extends TextRecognitionPlugin (still exposes scanText) and adds a recognizer property — the raw Nitro TextRecognizer HybridObject.
// v1
const { scanText }: TextRecognitionPlugin = useTextRecognition({ language: 'latin' })
// v2
const { scanText, recognizer }: TextRecognitionHandle = useTextRecognition({ language: 'latin' })The return type changed from TranslatorPlugin to TranslatorHandle.
TranslatorHandle exposes scanText, the async translate, and the raw HybridObjects recognizer / translator, plus the resolved from / to language strings.
// v1
const { translate }: TranslatorPlugin = useTranslate({ from: 'fr', to: 'en' })
// v2
const { scanText, translate, recognizer, translator, from, to }: TranslatorHandle =
useTranslate({ from: 'fr', to: 'en' })The biggest breaking change is how frames are consumed inside worklets. In v1, scanText(frame) or translate(frame) were called with the frame argument and both OCR and translation happened synchronously in the worklet. In v2, translation is async and the two steps are separated.
// v1
const { translate } = useTranslate({ from: 'fr', to: 'en' })
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const translated: string = translate(frame) // ← OCR + translate, all sync
runOnJS(setTranslated)(translated)
}, [translate])import { useTranslate } from 'react-native-vision-camera-ocr-plus'
import { useFrameOutput, useCameraDevice } from 'react-native-vision-camera'
import { scheduleOnRN } from 'react-native-worklets'
const { scanText, translate } = useTranslate({ from: 'fr', to: 'en' })
const frameOutput = useFrameOutput({
pixelFormat: 'rgb', // required on Android
onFrame: (frame) => {
'worklet'
const result = scanText(frame) // synchronous OCR — worklet-safe
if (result.resultText) {
translate(result.resultText) // async translation — JS thread
.then((translated) => scheduleOnRN(setTranslated, translated))
}
frame.dispose()
},
})Similarly, for OCR-only custom processors, useFrameProcessor is replaced by useFrameOutput in VisionCamera v5:
// v1
const { scanText } = useTextRecognition({ language: 'latin' })
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const text = scanText(frame)
runOnJS(setText)(text.resultText)
}, [scanText])// v2
import { scheduleOnRN } from 'react-native-worklets'
const { scanText } = useTextRecognition({ language: 'latin' })
const frameOutput = useFrameOutput({
pixelFormat: 'rgb', // required on Android
onFrame: (frame) => {
'worklet'
const result = scanText(frame)
if (result.resultText) {
scheduleOnRN(setText, result.resultText)
}
frame.dispose()
},
})The translate function on TranslatorPlugin / TranslatorHandle changed from a synchronous frame-accepting worklet to an async JS-thread function that accepts a plain string.
// v1 TranslatorPlugin
translate: (frame: Frame) => string // synchronous, worklet-safe
// v2 TranslatorPlugin / TranslatorHandle
translate: (text: string) => Promise<string> // async, JS threadIf you were calling translate(frame) in a worklet you must split the work into scanText(frame) (worklet) followed by translate(text) (JS thread via runOnJS / scheduleOnRN).
PhotoRecognizer now uses the Nitro TextRecognizer HybridObject instead of NativeModules.PhotoRecognizerModule. The public API is unchanged — the call signature and return type are the same:
const result: Text = await PhotoRecognizer({
uri: 'file:///path/to/photo.jpg',
orientation: 'portrait',
})No migration needed unless you were accessing the old PhotoRecognizerModule directly via NativeModules.
RemoveLanguageModel now uses the Nitro Translator HybridObject instead of NativeModules.RemoveLanguageModel. The public API is unchanged:
const success: boolean = await RemoveLanguageModel('fr')No migration needed unless you were accessing the old NativeModules.RemoveLanguageModel directly.
The worklet helper import changed from react-native-worklets-core to react-native-worklets:
// v1
import { useRunOnJS } from 'react-native-worklets-core'
// v2
import { scheduleOnRN, runOnJS } from 'react-native-worklets'Several types that were previously module-internal are now exported publicly. You can now import them directly:
import type {
BlockData,
LineData,
ElementData,
FrameType,
CornerPointsType,
// Nitro spec types
TextRecognizer,
Translator,
RecognizedText,
TextBlock,
TextLine,
TextElement,
BoundingFrame,
CornerPoint,
NitroScanRegion,
TextRecognitionConfig,
} from 'react-native-vision-camera-ocr-plus'Building for the iOS Simulator on Apple Silicon (arm64) may fail due to a known limitation in Google ML Kit which does not ship an arm64-simulator slice for some frameworks. The library works on physical iOS devices and on the iOS Simulator under Rosetta. See issue #91 for details.
If you have Firebase in your project, set your iOS Deployment Target to at least 16.0.
- Verify minimum platform versions: React Native ≥ 0.81, iOS ≥ 15.1, Android
minSdkVersion≥ 26 (targetSdkVersion ≥ 36), Expo ≥ 54 (if used) - Remove
react-native-worklets-core, addreact-native-nitro-modules,react-native-vision-camera-worklets, andreact-native-worklets - Upgrade
react-native-vision-camerato>=5.0.0 - Run
cd ios && pod install - Replace
useRunOnJSfromreact-native-worklets-corewithrunOnJS/scheduleOnRNfromreact-native-worklets - Replace
useFrameProcessorusage withuseFrameOutput(VisionCamera v5) - In translate mode: split
translate(frame)intoscanText(frame)(worklet) +translate(text)(JS thread) - Add
pixelFormat: 'rgb'touseFrameOutputon Android - Update any TypeScript types that referenced
TextRecognitionPlugin/TranslatorPlugin— the hook return types are nowTextRecognitionHandle/TranslatorHandle