npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@preeternal/react-native-document-scanner-plugin

v0.3.0

Published

Fork of react-native-document-scanner-plugin with New Architecture (TurboModule) support and active maintenance.

Downloads

1,041

Readme

@preeternal/react-native-document-scanner-plugin

npm version npm downloads

React Native document scanning for capture-first and analysis-ready workflows.

This fork started as a maintained alternative to the original react-native-document-scanner-plugin, but it has grown beyond a compatibility fork. Today it gives you a clean separation between:

  • capture — scan documents from camera UI;
  • analysis — extract barcode / OCR / semantics from existing images;
  • combined flows — scan and analyze in one call when your product needs both.

It is designed for apps that need more than “just scan a page”:

  • e-sign and contract flows
  • receipts and expense capture
  • logistics / proof-of-delivery
  • KYC and onboarding
  • forms, IDs, invoices, and document intake

Demo

https://github.com/user-attachments/assets/fda6d4d3-ac87-41d6-a04c-11f0ce08f5e8

Why this fork

The upstream package now supports New Architecture too, which is great. This package stays API-compatible for the core scan flow, while focusing on faster iteration and broader document-processing scenarios.

What is different from upstream now

This is no longer only about TurboModules parity. The fork has expanded into a broader mobile document workflow SDK with these additions:

  • separate public analysis APIs for existing images:
    • extractBarcodesFromImages(...)
    • extractTextFromImages(...)
    • analyzeScannedImages(...)
  • combined capture + analysis flow via scanAndAnalyzeDocument(...)
  • optional post-processing barcode extraction with Android build-time feature gating
  • OCR extraction with adaptive 180° fallback support
  • semantics-oriented analysis output:
    • text blocks
    • inferred tables
    • regions
    • structured data / key-value style fields
  • response sanitization on both platforms
  • Expo / EAS / CI hardening
  • opt-in native debug logs on iOS and Android
  • ongoing maintenance and release cadence

What this package is good at

Use it when you want a document capture layer that can also become the first stage of a richer pipeline:

  • camera capture
  • perspective-corrected scan results
  • analysis from already existing images
  • barcode extraction
  • OCR extraction
  • structured downstream payloads for app/backend workflows

Use your own or third-party tooling for final document authoring such as advanced PDF generation, searchable PDF composition, RTF export, signing, or storage pipelines.

Installation

yarn add @preeternal/react-native-document-scanner-plugin

iOS

  1. Open ios/Podfile and set platform :ios to 13 or higher.
  2. Add camera usage description to Info.plist:
    • NSCameraUsageDescription
  3. Install pods:
cd ios && bundle exec pod install && cd ..

Android

You do not need to request camera permission unless another plugin adds camera permission requirements to your app manifest. See Android Camera Permissions below.

Quick start

1. Scan a document

import React, { useEffect, useState } from 'react'
import { Image } from 'react-native'
import DocumentScanner from '@preeternal/react-native-document-scanner-plugin'

export default function App() {
  const [image, setImage] = useState<string | undefined>()

  useEffect(() => {
    const run = async () => {
      const { status, scannedImages } = await DocumentScanner.scanDocument({
        responseType: 'imageFilePath',
      })

      if (status === 'success' && scannedImages.length > 0) {
        setImage(scannedImages[0])
      }
    }

    run()
  }, [])

  return (
    <Image
      resizeMode="contain"
      style={{ width: '100%', height: '100%' }}
      source={image ? { uri: image } : undefined}
    />
  )
}

2. Extract barcodes from existing images

const result = await DocumentScanner.scanDocument({ responseType: 'imageFilePath' })

const barcodes = await DocumentScanner.extractBarcodesFromImages(
  result.scannedImages,
  {
    barcodeFormats: ['ean13', 'itf'],
    concurrency: 2,
  }
)

3. Extract OCR text from existing images

const textBlocks = await DocumentScanner.extractTextFromImages(images, {
  concurrency: 2,
  ocrRotate180Fallback: true,
})

If you only need plain text per page, use the text field on each block — it is already the full concatenated string for that image:

const pages = textBlocks.map((block) => ({
  pageIndex: block.sourceImageIndex,
  text: block.text,
}))
// [{ pageIndex: 0, text: "Invoice #1234\nTotal: $99.00\n..." }, ...]

The lines and bbox fields are there for advanced layout use cases (PDF composition, field highlighting, etc.) and can be ignored when you only need the text.

4. Run full analysis over scanned images

const analysis = await DocumentScanner.analyzeScannedImages(images, {
  extract: {
    barcodes: true,
    text: true,
    tables: true,
    structuredData: true,
  },
  concurrency: 2,
})

5. Scan and analyze in one call

const result = await DocumentScanner.scanAndAnalyzeDocument({
  responseType: 'imageFilePath',
  analysis: {
    extract: {
      barcodes: true,
      text: true,
    },
    concurrency: 2,
  },
})

API overview

Capture

  • scanDocument(options?)

Open native document scanner UI and return sanitized images.

Targeted analysis

  • extractBarcodesFromImages(images, options?)
  • extractTextFromImages(images, options?)

Run only the stage you need for lower latency and smaller payloads.

Combined analysis

  • analyzeScannedImages(images, options)
  • scanAndAnalyzeDocument(options)

Use these when your app needs richer OCR and document understanding output.

Recommended usage modes

Simple mode

Request only the data your screen or business flow needs. Examples:

{ extract: { barcodes: true } }
{ extract: { text: true } }

This keeps latency and payload size lower.

Full mode

Request richer OCR semantics when you need downstream processing:

{
  extract: {
    text: true,
    tables: true,
    regions: true,
    structuredData: true,
  }
}

This is useful for receipts, forms, invoice parsing, document intake, or backend enrichment.

Analysis guidance

Use analyzeScannedImages(...) when your pipeline needs structured OCR metadata rather than just raw capture output.

Typical patterns:

  • UI-driven flows (fast, lightweight): request only what is needed:

    • extract: { barcodes: true }
    • extract: { text: true }
  • Document-processing flows (backend enrichment, intake pipelines): request richer semantics:

    • text
    • tables
    • regions
    • structuredData

Notes:

  • Full OCR payload is intended as a downstream foundation for searchable PDF generation, backend enrichment, invoice parsing, receipt processing, or custom document workflows.
  • This library returns capture and analysis artifacts. Final document authoring (PDF composition, signing workflows, storage pipelines, or export formats such as RTF/searchable PDF) is expected to be handled by your app or third‑party tooling.

Android feature gating for analysis

Barcode / OCR / table-related analysis features are opt-in on Android. This keeps the core scanning package lean for apps that only need capture.

Enable features when building Android:

./gradlew :react-native-document-scanner-plugin:assemble -PDocumentScanner_analysisFeatures=barcode

Or configure android/gradle.properties:

DocumentScanner_analysisFeatures=barcode

Accepted values:

  • barcode
  • text
  • tables
  • comma-separated combinations such as barcode,text
  • all
  • none

If a feature is not enabled in the Android native build, the corresponding API returns not_enabled style behavior or rejects with a feature-specific error such as barcode_not_enabled.

Expo / EAS: configure via app.json

For Expo managed and bare workflows, use the built-in config plugin instead of editing gradle.properties manually:

{
  "expo": {
    "plugins": [
      [
        "@preeternal/react-native-document-scanner-plugin",
        {
          "analysisFeatures": "barcode,text"
        }
      ]
    ]
  }
}

Accepted values for analysisFeatures: barcode, text, tables, comma-separated combinations, all, or none (default when omitted).

The plugin writes DocumentScanner_analysisFeatures to android/gradle.properties during expo prebuild / EAS build. iOS requires no configuration — analysis features are always available.

iOS behavior: real device vs simulator

The library supports both, but analysis quality can differ by environment.

| Environment | Primary API path | Notes | | ----------- | ---------------- | ----- | | Real device (modern iOS) | RecognizeDocumentsRequest when available | Best reference for final quality validation | | Simulator (modern iOS) | Modern path may run, but Vision results can differ | Use for integration, not final barcode quality decisions | | Older iOS versions | Legacy Vision fallback path | Stable compatibility-focused behavior |

Practical guidance

  • Use Simulator for fast integration work.
  • Validate barcode and OCR quality on a real device before release.
  • If Simulator and device differ, trust real-device output.

Why this matters

Apple Vision APIs can behave differently between Simulator and real hardware, especially for barcode detection.

Practical expectations:

  • Simulator is reliable for integration and iteration.
  • Real devices should be used for validating OCR/barcode quality before release.
  • If Simulator and device outputs differ, treat real-device output as the source of truth for tuning and production decisions.

Response sanitization

Returned scannedImages are sanitized natively on both platforms, so you do not need to manually post-filter invalid entries in JS.

  • Android:
    • base64 responses are filtered to non-empty strings
    • URI responses are checked for readability through ContentResolver
  • iOS:
    • strings are trimmed
    • file:// URLs are normalized
    • file existence is checked before returning

Example:

const { status, scannedImages } = await DocumentScanner.scanDocument({
  responseType: 'imageFilePath',
})

if (status === 'success' && scannedImages.length > 0) {
  // safe to use
  setImage(scannedImages[0])
}

Documentation

scanDocument(...)

scanDocument(options?: ScanDocumentOptions | undefined) => Promise<ScanDocumentResponse>

Opens native camera UI and starts document scanning.

| Param | Type | | ------------- | ------------------------------------------------------------------- | | options | ScanDocumentOptions |

Returns: Promise<ScanDocumentResponse>

extractBarcodesFromImages(...)

extractBarcodesFromImages(
  images: string[],
  options?: ExtractBarcodesFromImagesOptions | undefined
) => Promise<Barcode[]>

Extracts barcodes from existing images without opening scanner UI.

| Param | Type | | ------------- | ------------------------------------------------------------------------------------------------------ | | images | string[] | | options | ExtractBarcodesFromImagesOptions |

Returns: Promise<Barcode[]>

extractTextFromImages(...)

extractTextFromImages(
  images: string[],
  options?: ExtractTextFromImagesOptions | undefined
) => Promise<TextBlock[]>

Extracts OCR text blocks from existing images without opening scanner UI.

| Param | Type | | ------------- | ------------------------------------------------------------------------------------------------------ | | images | string[] | | options | ExtractTextFromImagesOptions |

Returns: Promise<TextBlock[]>

Notes:

  • Android requires DocumentScanner_analysisFeatures to include text or tables.
  • iOS is available by default.
  • ocrRotate180Fallback is optional for this method and defaults to false.
  • textTimeoutMs (Android-only) can override per-image OCR timeout. Default: 25000ms.

analyzeScannedImages(...)

analyzeScannedImages(
  images: string[],
  options: AnalyzeScannedImagesOptions
) => Promise<AnalysisResult>

Universal post-processing over scanned images. Barcode extraction, OCR text, table inference, region inference, and structured data extraction run natively.

ocrRotate180Fallback is enabled by default for this method (true) and only performs an extra 180° OCR pass when the first pass is weak.

Advanced timeout options:

  • barcodeTimeoutMs (Android-only): per-image barcode extraction timeout. Default 10000ms.
  • textTimeoutMs (Android-only): per-image OCR extraction timeout. Default 25000ms.
  • Values are clamped natively to a safe range.
const analysis = await DocumentScanner.analyzeScannedImages(scannedImages, {
  extract: { barcodes: true },
  barcodeFormats: ['qr', 'ean13'],
  concurrency: 2,
})

Returns: Promise<AnalysisResult>

scanAndAnalyzeDocument(...)

scanAndAnalyzeDocument(
  options: ScanAndAnalyzeDocumentOptions
) => Promise<ScanAndAnalyzeDocumentResponse>

Convenience API for one-call flow:

scanDocument(...) -> analyzeScannedImages(...)
const result = await DocumentScanner.scanAndAnalyzeDocument({
  responseType: 'imageFilePath',
  analysis: {
    extract: { barcodes: true },
    barcodeFormats: ['qr'],
    concurrency: 2,
  },
})

Returns: Promise<ScanAndAnalyzeDocumentResponse>

Interfaces

ScanDocumentResponse

  • scannedImages: string[] — Array of valid file URIs or base64 strings, already sanitized by the native module.
  • status: ScanDocumentResponseStatus — Indicates whether scan completed successfully or was cancelled by the user.

ScanDocumentOptions

  • croppedImageQuality: number — Cropped image quality from 0 to 100. Default: 100.
  • maxNumDocuments: number — Android only: maximum number of captured pages. Default: undefined.
  • responseType: ResponseType — Result format on success. Default: ResponseType.ImageFilePath.

ExtractBarcodesFromImagesOptions

  • barcodeFormats: Barcode['format'][] — Optional allow-list of normalized barcode formats. Default: undefined.
  • concurrency: 1 | 2 — Maximum native worker concurrency. Clamped to 1..2. Default: 2.
  • barcodeTimeoutMs: number — Android-only per-image barcode extraction timeout (milliseconds). Default: 10000.

ExtractTextFromImagesOptions

  • concurrency: 1 | 2 — Maximum native worker concurrency. Clamped to 1..2. Default: 2.
  • ocrRotate180Fallback: boolean — Run an extra 180° OCR pass only when the first pass returns too little text. Default: false.
  • textTimeoutMs: number — Android-only per-image OCR extraction timeout (milliseconds). Default: 25000.

AnalyzeExtractOptions

  • barcodes: boolean — Enable barcode extraction stage.
  • text: boolean — Enable OCR text extraction stage.
  • tables: boolean — Enable table inference from OCR lines.
  • regions: boolean — Enable zone or region inference from OCR blocks.
  • structuredData: boolean — Enable entity and key-value inference from OCR output.

AnalyzeScannedImagesOptions

  • extract: AnalyzeExtractOptions — Extractor toggles for image analysis.
  • barcodeFormats: Barcode['format'][] — Optional barcode format allow-list.
  • concurrency: 1 | 2 — Native worker concurrency for barcode/OCR stages.
  • ocrRotate180Fallback: boolean — Adaptive OCR fallback for text/semantics stages. Defaults to true.
  • barcodeTimeoutMs: number — Android-only per-image barcode extraction timeout (milliseconds). Default: 10000.
  • textTimeoutMs: number — Android-only per-image OCR extraction timeout (milliseconds). Default: 25000.

AnalysisResult

  • status: 'success' | 'partial' | 'failed' | 'not_enabled' — Aggregate status of requested analysis stages.
  • barcodes: Barcode[] — Extracted barcodes when requested and available.
  • textBlocks: TextBlock[] — Canonical OCR text block field.
  • text: TextBlock[] — Backward-compatible alias for textBlocks.
  • tables: TableBlock[] — Inferred tables from OCR lines.
  • regions: Region[] — Inferred document zones.
  • structuredData: StructuredData — Inferred entities and key-value style fields.

ScanAndAnalyzeDocumentOptions

  • analysis: AnalyzeScannedImagesOptions — Analysis options for post-processing.
  • croppedImageQuality: number — Same as ScanDocumentOptions.
  • maxNumDocuments: number — Same as ScanDocumentOptions (Android only).
  • responseType: ResponseType — Same as ScanDocumentOptions.

ScanAndAnalyzeDocumentResponse

Barcode

  • value: string — Decoded barcode payload.
  • format: 'aztec' | 'codabar' | 'code39' | 'code93' | 'code128' | 'dataMatrix' | 'ean8' | 'ean13' | 'itf' | 'pdf417' | 'qr' | 'upca' | 'upce' | 'unknown' — Normalized barcode format.
  • sourceImageIndex: number — Index of source image in scannedImages.

TextBlock

  • text: string — Full OCR text for this page. One block per source image.
  • sourceImageIndex: number — Source image index in scannedImages.
  • bbox: { left: number; top: number; width: number; height: number } — Optional normalized bounding box (0..1).
  • lines: { text: string; bbox?: { left: number; top: number; width: number; height: number } }[] — OCR lines inside the block when available.

TableBlock

  • sourceImageIndex: number — Source image index in scannedImages.
  • rows: string[][] — Inferred table rows and cell texts.
  • bbox: { left: number; top: number; width: number; height: number } — Optional normalized table bounds.

Region

  • type: 'header' | 'footer' | 'paragraph' | 'signature' | 'stamp' | 'unknown' — Inferred region category.
  • sourceImageIndex: number — Source image index in scannedImages.
  • bbox: { left: number; top: number; width: number; height: number } — Normalized region bounds (0..1).
  • score: number — Optional heuristic confidence score.

StructuredData

  • entities: { type: 'phone' | 'email' | 'date' | 'amount' | 'id' | 'unknown'; value: string; sourceImageIndex: number }[] — Inferred typed entities.
  • fields: { key: string; value: string }[] — Inferred key-value fields from OCR lines.

Normalization guarantees:

  • date values are normalized to ISO-like YYYY-MM-DD when parser confidence is sufficient.
  • amount values are normalized to CODE value when currency can be inferred.
  • phone values are normalized to compact digit form, preserving + when present.
  • email values are lowercased.
  • id values are uppercased and compacted.
  • fields[].key is sanitized to lowercase snake-like form on both platforms.

Best-effort quality note:

  • Output shape is stable across platforms.
  • Extraction quality may still vary depending on OS/runtime capabilities.

Enums

ScanDocumentResponseStatus

| Member | Value | Description | | :-------------- | :---------------- | :------------------------------------------------- | | Success | 'success' | Scan completed successfully. | | Cancel | 'cancel' | User closed the scanner before completing the flow.|

ResponseType

| Member | Value | Description | | :-------------- | :---------------- | :----------------------------------------------- | | Base64 | 'base64' | Return scanned images as base64 strings. | | ImageFilePath | 'imageFilePath' | Return scanned images as image file paths. |

Common mistakes

Android Camera Permissions

You do not need to request camera permissions unless another camera plugin adds:

<uses-permission android:name="android.permission.CAMERA" />

If that permission is present and not granted, you can get errors like:

Error: error - error opening camera: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE

Example permission request flow:

import React, { useEffect, useState } from 'react'
import { Alert, Image, PermissionsAndroid, Platform } from 'react-native'
import DocumentScanner from '@preeternal/react-native-document-scanner-plugin'

export default function App() {
  const [image, setImage] = useState<string | undefined>()

  useEffect(() => {
    const run = async () => {
      if (
        Platform.OS === 'android' &&
        (await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA)) !==
          PermissionsAndroid.RESULTS.GRANTED
      ) {
        Alert.alert('Error', 'User must grant camera permissions to use document scanner.')
        return
      }

      const { status, scannedImages } = await DocumentScanner.scanDocument()

      if (status === 'success' && scannedImages.length > 0) {
        setImage(scannedImages[0])
      }
    }

    run()
  }, [])

  return (
    <Image
      resizeMode="contain"
      style={{ width: '100%', height: '100%' }}
      source={image ? { uri: image } : undefined}
    />
  )
}

Migrating between upstream and this fork

Both packages are compatible for the core scan flow (scanDocument(...)).

From this fork to upstream

yarn remove @preeternal/react-native-document-scanner-plugin
yarn add react-native-document-scanner-plugin
cd ios && pod install && cd -

From upstream to this fork

yarn remove react-native-document-scanner-plugin
yarn add @preeternal/react-native-document-scanner-plugin
cd ios && pod install && cd -

Roadmap direction

The current direction is to keep the package strong in the capture + analysis layer rather than turning it into a full document platform.

Areas that fit this package especially well:

  • multi-page document flows
  • image normalization and cleanup
  • richer structured result payloads
  • barcode / OCR / semantics as focused post-processing stages
  • integration examples for upload, intake, logistics, and document workflows

Contributing

Credits

This project builds on the excellent work by WebsiteBeaver/react-native-document-scanner-plugin. The original repository is MIT-licensed, and original copyright notices are preserved in this fork’s LICENSE.

Barcode extraction logic in this repository was adapted from:

License

MIT