[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81544":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":15,"stars7d":16,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":15,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":18,"hasPages":18,"topics":20,"createdAt":10,"pushedAt":10,"updatedAt":30,"readmeContent":31,"aiSummary":32,"trendingCount":15,"starSnapshotCount":15,"syncStatus":13,"lastSyncTime":33,"discoverSource":34},81544,"expo-mlkit-ocr","rbayuokt\u002Fexpo-mlkit-ocr","rbayuokt","Production-ready Expo Module for on-device text recognition (OCR) using Google ML Kit Text Recognition v2 + OCRTextOverlay","https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fexpo-mlkit-ocr",null,"TypeScript",27,2,26,0,1,1.43,false,"main",[21,22,5,23,24,25,26,27,28,29],"expo","expo-mlkit","expo-ocr","expo-text-recognition","mlkit","ocr","react-native","react-native-ocr","text-recognition","2026-06-12 02:04:16","![expo-mlkit-ocr](docs\u002Fexpo-mlkit-ocr.png)\n\n# expo-mlkit-ocr\n\nProduction-ready Expo Module for on-device text recognition (OCR) using **Google ML Kit Text Recognition v2** for both iOS and Android.\n\n## Preview\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"docs\u002Fandroid.png\" height=\"500\" alt=\"Android Preview\" \u002F>\n  \u003Cimg src=\"docs\u002Fios.png\" height=\"500\" alt=\"iOS Preview\" \u002F>\n\u003C\u002Fdiv>\n\n## Features\n\n- ✅ **On-device OCR** — no network requests required\n- ✅ **ML Kit v2** — official Google ML Kit standalone (not Firebase ML Kit)\n- ✅ **Structured output** — blocks, lines, and elements with bounding boxes\n- ✅ **iOS & Android** — native implementations using Expo Modules API\n- ✅ **TypeScript support** — fully typed API\n- ✅ **Expo Config Plugin** — automatic native setup\n- ✅ **Device support check** — `isSupported()` to detect device compatibility before OCR\n- ✅ **Interactive overlay** — `\u003COCRTextOverlay>` component for visualizing & selecting recognized text\n\n## Installation\n\n```bash\nnpx expo install expo-mlkit-ocr expo-image-picker expo-build-properties\n```\n\n> ⚠️ **`expo-build-properties` is REQUIRED** to set the iOS deployment target to `16.0` (needed by Google ML Kit on iOS).\n\n## Setup\n\n### 1. Configure `app.json` \u002F `app.config.js`\n\nAdd **both plugins** to your config:\n\n```json\n{\n  \"expo\": {\n    \"plugins\": [\n      [\n        \"expo-mlkit-ocr\",\n        {\n          \"iosEngine\": \"auto\"\n        }\n      ],\n      [\n        \"expo-build-properties\",\n        {\n          \"ios\": {\n            \"deploymentTarget\": \"16.0\"\n          }\n        }\n      ]\n    ]\n  }\n}\n```\n\n> ⚠️ **Important:** Without `deploymentTarget: \"16.0\"` and `useFrameworks: \"static\"`, you will get:\n> ```\n> ERROR [runtime not ready]: Error: Cannot find native module 'ExpoMlkitOcr'\n> ```\n\n### 2. Build Your App\n\n**With EAS Build:**\n\n```bash\neas build --platform ios\neas build --platform android\n```\n\n**Or with local prebuild:**\n\n```bash\nnpx expo prebuild --clean\nnpx expo run:ios\n# or\nnpx expo run:android\n```\n\n> ❌ **Will NOT work in Expo Go** — this is a custom native module. You need a development client or EAS Build.\n\n### Troubleshooting\n\n**`Cannot find native module 'ExpoMlkitOcr'`**\n- Did you add `expo-build-properties` plugin with `deploymentTarget: \"16.0\"`? ⚠️ Most common cause\n- Did you run `npx expo prebuild --clean` after installing?\n- Are you running on a development client (not Expo Go)?\n\n**Build fails on iOS Simulator (arm64)**\n- Use `iosEngine: \"auto\"` or `\"vision\"` in plugin config (uses Apple Vision instead of ML Kit)\n\n## Usage\n\n### Basic Example\n\n```typescript\nimport { recognizeText } from 'expo-mlkit-ocr';\nimport * as ImagePicker from 'expo-image-picker';\n\nasync function pickAndRecognize() {\n  const result = await ImagePicker.launchImageLibraryAsync({\n    mediaTypes: ['images'],\n  });\n\n  if (result.canceled || !result.assets[0]) return;\n\n  try {\n    const recognition = await recognizeText(result.assets[0].uri);\n    console.log('Recognized text:', recognition.text);\n    console.log('Blocks:', recognition.blocks);\n  } catch (error) {\n    console.error('Recognition failed:', error);\n  }\n}\n```\n\n### Output Format\n\nThe function returns a `RecognitionResult` object with this structure:\n\n```typescript\nexport type RecognitionResult = {\n  text: string; \u002F\u002F Full recognized text\n  blocks: TextBlock[];\n};\n\nexport type TextBlock = {\n  text: string;\n  boundingBox: {\n    x: number;\n    y: number;\n    width: number;\n    height: number;\n  };\n  lines: TextLine[];\n};\n\nexport type TextLine = {\n  text: string;\n  boundingBox: {\n    x: number;\n    y: number;\n    width: number;\n    height: number;\n  };\n  elements: TextElement[];\n};\n\nexport type TextElement = {\n  text: string;\n  boundingBox: {\n    x: number;\n    y: number;\n    width: number;\n    height: number;\n  };\n};\n```\n\n**Bounding box coordinates** are in the image's native coordinate system (top-left origin):\n- `x`, `y` — top-left corner\n- `width`, `height` — dimensions in pixels\n\n### Example Output\n\n```json\n{\n  \"text\": \"Hello World\\nExample Text\",\n  \"blocks\": [\n    {\n      \"text\": \"Hello World\",\n      \"boundingBox\": { \"x\": 100, \"y\": 50, \"width\": 200, \"height\": 50 },\n      \"lines\": [\n        {\n          \"text\": \"Hello World\",\n          \"boundingBox\": { \"x\": 100, \"y\": 50, \"width\": 200, \"height\": 50 },\n          \"elements\": [\n            {\n              \"text\": \"Hello\",\n              \"boundingBox\": { \"x\": 100, \"y\": 50, \"width\": 80, \"height\": 50 }\n            },\n            {\n              \"text\": \"World\",\n              \"boundingBox\": { \"x\": 190, \"y\": 50, \"width\": 110, \"height\": 50 }\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\n\n## API Reference\n\n### `recognizeText(uri: string): Promise\u003CRecognitionResult>`\n\nRecognizes text from an image at the provided URI.\n\n**Parameters:**\n- `uri` (string) — file URI or content URI to the image (e.g., from `expo-image-picker`)\n\n**Returns:**\n- `Promise\u003CRecognitionResult>` — structured text recognition result\n\n**Errors:**\n- `INVALID_URI` — provided URI is not valid\n- `IMAGE_LOAD_FAILED` — image could not be loaded from the URI\n- `RECOGNITION_FAILED` — text recognition failed (rare)\n\n### `isSupported(): boolean`\n\nChecks if the device supports ML Kit text recognition (OCR).\n\nReturns `true` if the device meets the minimum OS requirements; `false` otherwise. Call this before attempting `recognizeText()` to provide graceful fallback UI for unsupported devices.\n\n**Minimum OS versions:**\n- **iOS**: 16.0+\n- **Android**: API 21+ (Android 5.0)\n\n**Example:**\n\n```typescript\nimport { isSupported, recognizeText } from 'expo-mlkit-ocr';\n\nexport default function App() {\n  if (!isSupported()) {\n    return \u003CText>OCR is not supported on this device.\u003C\u002FText>;\n  }\n\n  return (\n    \u003CButton\n      title=\"Pick Image & Recognize\"\n      onPress={async () => {\n        const result = await recognizeText(imageUri);\n        console.log(result.text);\n      }}\n    \u002F>\n  );\n}\n```\n\n### `\u003COCRTextOverlay \u002F>` — Interactive Bounding Box Overlay\n\nRenders interactive bounding boxes over an image to visualize OCR results. Tap boxes to select text and trigger a callback (e.g., copy to clipboard).\n\n**Usage:**\n\n```typescript\nimport { recognizeText, OCRTextOverlay } from 'expo-mlkit-ocr';\nimport { Image, Clipboard } from 'react-native';\nimport { useState } from 'react';\n\nexport default function App() {\n  const [result, setResult] = useState(null);\n\n  async function pickAndRecognize() {\n    const picked = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'] });\n    if (!picked.assets[0]) return;\n\n    const asset = picked.assets[0];\n    const ocrResult = await recognizeText(asset.uri);\n    setResult(ocrResult);\n  }\n\n  return (\n    \u003C>\n      \u003CButton title=\"Pick & Recognize\" onPress={pickAndRecognize} \u002F>\n\n      {result && (\n        \u003COCRTextOverlay\n          result={result}\n          imageWidth={picked.assets[0].width}\n          imageHeight={picked.assets[0].height}\n          highlightLevel=\"line\"\n          onSelect={(item) => Clipboard.setString(item.text)}\n        >\n          \u003CImage source={{ uri: picked.assets[0].uri }} style={{ width: 300, height: 400 }} \u002F>\n        \u003C\u002FOCRTextOverlay>\n      )}\n    \u003C\u002F>\n  );\n}\n```\n\n**Props:**\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `result` | `RecognitionResult` | — | OCR result from `recognizeText()` |\n| `imageWidth` | `number` | — | Native image width (pixels) |\n| `imageHeight` | `number` | — | Native image height (pixels) |\n| `children` | `ReactNode` | — | Image component to wrap |\n| `highlightLevel` | `'block' \\| 'line' \\| 'element'` | `'line'` | Which level to highlight |\n| `resizeMode` | `'contain' \\| 'cover'` | `'contain'` | Image resize behavior |\n| `boxColor` | `string` | `'#00BFFF'` | Box border & fill color (hex) |\n| `selectedBoxColor` | `string` | `'#FF6347'` | Color when a box is selected |\n| `boxOpacity` | `number` | `0.25` | Fill opacity (0–1) |\n| `strokeWidth` | `number` | `2` | Border width (pixels) |\n| `cornerRadius` | `number` | `4` | Rounded corner radius (pixels) |\n| `multiSelect` | `boolean` | `true` | Allow selecting multiple boxes |\n| `onSelect` | `(item) => void` | — | Callback when box(es) are tapped (single item or array) |\n| `style` | `ViewStyle` | — | Optional wrapper style |\n\n**Multi-Select Example:**\n\n```typescript\n\u002F\u002F Single selection (one box at a time)\n\u003COCRTextOverlay\n  result={result}\n  imageWidth={1920}\n  imageHeight={1080}\n  multiSelect={false}  \u002F\u002F Only one selection\n  onSelect={(item) => console.log('Selected:', item.text)}\n>\n  \u003CImage source={{ uri }} \u002F>\n\u003C\u002FOCRTextOverlay>\n\n\u002F\u002F Multiple selection (tap multiple boxes)\n\u003COCRTextOverlay\n  result={result}\n  imageWidth={1920}\n  imageHeight={1080}\n  multiSelect={true}  \u002F\u002F Default - allow multiple selections\n  onSelect={(items) => {\n    if (Array.isArray(items)) {\n      console.log('Selected items:', items.map(i => i.text).join(', '));\n    } else {\n      console.log('Single item:', items.text);\n    }\n  }}\n>\n  \u003CImage source={{ uri }} \u002F>\n\u003C\u002FOCRTextOverlay>\n```\n\n**Highlights:**\n- ✅ Pure React Native (no external canvas library)\n- ✅ Tap to toggle selection + visual highlight\n- ✅ Multi-select mode: tap multiple boxes, all stay highlighted\n- ✅ Single-select mode: only one box highlighted at a time\n- ✅ Works with any `\u003CImage>` component (react-native, expo-image, etc.)\n- ✅ Automatic coordinate scaling for `contain` and `cover` resize modes\n- ✅ Full TypeScript support\n\n## Common Errors\n\n### `Error: expo-mlkit-ocr is not supported on web.`\n\nThe module only works on iOS and Android. For web support, use a third-party OCR service (e.g., Tesseract.js).\n\n```typescript\nimport { Platform } from 'react-native';\nimport { recognizeText } from 'expo-mlkit-ocr';\n\nif (Platform.OS !== 'web') {\n  const result = await recognizeText(uri);\n} else {\n  \u002F\u002F Use a web-based OCR service\n}\n```\n\n### `IMAGE_LOAD_FAILED`\n\n- Ensure the URI is valid and the file exists\n- Use URIs from `expo-image-picker` or `expo-camera` which are guaranteed to work\n- On Android, both `file:\u002F\u002F` and `content:\u002F\u002F` URIs are supported\n\n### iOS Simulator (arm64) + iOS 26.x\n\nGoogle ML Kit CocoaPods binaries exclude the `ios-arm64-simulator` slice, so arm64-only simulator runtimes (for example iOS 26.x on Apple Silicon) can’t link ML Kit frameworks and will fail at build\u002Flink time.\n\nTo keep development on simulator unblocked, `expo-mlkit-ocr` supports **Apple Vision** as a fallback OCR engine on iOS. The **JavaScript response shape stays the same** (`text`, `blocks`, `lines`, `elements`, `boundingBox`); only the underlying OCR engine changes.\n\n```json\n{\n  \"expo\": {\n    \"plugins\": [\n      [\"expo-mlkit-ocr\", { \"iosEngine\": \"vision\" }]\n    ]\n  }\n}\n```\n\nSupported `iosEngine` values:\n- `\"auto\"` (default): use Apple Vision (simulator-friendly default)\n- `\"mlkit\"`: use Google ML Kit (won’t build on arm64-only iOS Simulator runtimes)\n- `\"vision\"`: always use Apple Vision (disables ML Kit pods)\n\nLegacy option (equivalent to `iosEngine: \"vision\"`):\n- `disableMlkitOnSimulator: true`\n\n## Development\n\n### Project Structure\n\n```\nexpo-mlkit-ocr\u002F\n├── src\u002F                          # TypeScript source\n│   ├── index.ts\n│   ├── ExpoMlkitOcr.types.ts\n│   ├── ExpoMlkitOcrModule.ts\n│   ├── ExpoMlkitOcrModule.web.ts\n│   └── OCRTextOverlay.tsx        # Interactive overlay component\n├── ios\u002F\n│   ├── ExpoMlkitOcrModule.swift   # ML Kit integration\n│   └── ExpoMlkitOcr.podspec\n├── android\u002F\n│   ├── build.gradle\n│   └── src\u002Fmain\u002Fjava\u002F...\u002FExpoMlkitOcrModule.kt\n├── app.plugin.js                 # Expo config plugin entry\n├── plugins\u002F\n│   └── withMlkitSimulatorArm64Fix.js\n├── example\u002F                      # Example app\n│   ├── App.tsx\n│   └── app.json\n└── expo-module.config.json\n```\n\n### Building from Source\n\n```bash\n# Install dependencies\nnpm install\n\n# Build the module (src\u002F → build\u002F)\nnpm run prepare\n\n# Open the example app (iOS)\nnpm run open:ios\n\n# Or run the example app with Expo CLI\ncd example\nnpx expo start\n\n# Scan QR code with Expo Go or run on device\u002Fsimulator\n```\n\n### Running the Example App\n\nThe example app at `example\u002F` demonstrates:\n1. Picking an image from the device library\n2. Running OCR with `recognizeText()`\n3. Displaying the results (full text, blocks, lines, elements)\n\n## License\n\nMIT\n\n## Contributing\n\nContributions are welcome! Please ensure:\n- TypeScript code compiles without errors\n- Native code follows platform conventions\n- Example app works on both iOS and Android\n\n## References\n\n- [ML Kit Text Recognition (v2) — iOS](https:\u002F\u002Fdevelopers.google.com\u002Fml-kit\u002Fvision\u002Ftext-recognition\u002Fv2\u002Fios)\n- [ML Kit Text Recognition (v2) — Android](https:\u002F\u002Fdevelopers.google.com\u002Fml-kit\u002Fvision\u002Ftext-recognition\u002Fv2\u002Fandroid)\n- [Expo Modules API](https:\u002F\u002Fdocs.expo.dev\u002Fmodules\u002Fget-started\u002F)\n\nMade with ❤️ by [rbayuokt](https:\u002F\u002Fgithub.com\u002Frbayuokt)\n","expo-mlkit-ocr 是一个用于设备端文本识别（OCR）的生产就绪型 Expo 模块，基于 Google ML Kit Text Recognition v2。该项目支持 iOS 和 Android 平台，提供完全本地化的文本识别功能，无需网络请求。它利用了最新的 ML Kit 技术，确保高性能和准确性，并且提供了结构化的输出，包括带有边界框的文本块、行等信息。此外，项目还包含了 TypeScript 支持、自动配置插件以及设备兼容性检查等功能，便于开发者集成。适用于需要在移动应用中实现快速准确的文字识别场景，如文档扫描、名片读取等。","2026-06-11 04:05:26","CREATED_QUERY"]