Scanbot SDK has been acquired by Apryse! Learn more

Learn more
Skip to content

How to build a Capacitor document scanner app with ML Kit

Kevin October 28, 2025 12 mins read
Capacitor ML Kit document scanner tutorial

In this tutorial, you’ll learn how to build a cross-platform mobile app for scanning documents using Capacitor and Google’s ML Kit.

To integrate the scanning functionalities, you’ll use @capgo/capacitor-document-scanner, a wrapper around Google’s ML Kit that streamlines its integration into Capacitor projects.

Scanning a document with our Capacitor ML Kit document scanning app

Building the app requires the following steps:

  1. Setting up the project
  2. Installing the document scanner plugin
  3. Setting up Android & iOS permissions
  4. Implementing the document scanning feature

Prerequisites

Before getting started, make sure your environment is ready.

Core requirements:

  • Node.js version 20.19 or higher

For Android development:

  • Android Studio Jellyfish (2024.2.1) or higher
  • Android SDK (API 22 or higher), Platforms and Developer Tools

For iOS development:

  • macOS with Xcode 16 or higher
  • Homebrew
  • CocoaPods (brew install cocoapods)
  • Xcode Command Line Tools (xcode-select --install)

Since scanning documents requires camera access, we recommend testing the app on a physical device.

Developing Capacitor applications can be done via the CLI or using the VS Code extension. You also have the option to use Capacitor with different frameworks. In this tutorial, we’ll use CLI commands and Capacitor with React. However, feel free to follow along using whatever option suits you best.

Step 1: Set up the project

First, create a new React project using Vite.

npm create vite@latest document-scanner-app -- --template react

Then navigate into your project directory.

cd document-scanner-app

Next, install the Capacitor core JavaScript runtime and Command Line Interface:

npm install @capacitor/core @capacitor/cli

And initialize the Capacitor configuration:

npx cap init

Enter your app’s name (e.g., “Document Scanner App”) and App package ID (e.g., “com.documentscanner.app”) when prompted.

Now, add Android and iOS as platforms:

npm install @capacitor/android @capacitor/ios
npx cap add android
npx cap add ios

Step 2: Install the document scanner plugin

Now you can install the @capgo/capacitor-document-scanner package via npm.

npm install @capgo/capacitor-document-scanner

Step 3: Set up Android & iOS permissions

To configure camera access for Android, open android/app/src/main/AndroidManifest.xml and add this line to the <manifest> tag:

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

For iOS, open ios/App/Podfile and set the deployment target to at least 15.5 (this prevents CocoaPods version issues).

platform :ios, '15.5'

Then open ios/App/App/Info.plist and add the camera permission description inside the <dict> block.

<key>NSCameraUsageDescription</key>
<string>Grant camera access to scan documents</string>

Step 4: Implement the document scanning feature

Create a new file src/components/DocumentScannerView.jsx and paste the following code to set up a document scanner component:

import React, { useState } from 'react'
import { Capacitor } from '@capacitor/core'
import { DocumentScanner } from '@capgo/capacitor-document-scanner'

export default function DocumentScannerView() {
  const [imageSrc, setImageSrc] = useState(null)
  const [scanning, setScanning] = useState(false)

  const scanDocument = async () => {
    try {
      setScanning(true)
      const { scannedImages, status } = await DocumentScanner.scanDocument()

      if (status === 'cancel') {
        alert('Scan cancelled.')
        return
      }

      if (scannedImages.length > 0) {
        const firstImage = Capacitor.convertFileSrc(scannedImages[0])
        setImageSrc(firstImage)
      }
    } catch (err) {
      console.error(err)
      alert('Error while scanning: ' + err.message)
    } finally {
      setScanning(false)
    }
  }

  return (
    <div style={{ textAlign: 'center', padding: 20 }}>
      <h2>📄 Document Scanner</h2>
      {!scanning && (
        <button
          onClick={scanDocument}
          style={{
            background: '#007bff',
            color: 'white',
            padding: '10px 20px',
            border: 'none',
            borderRadius: '6px',
            cursor: 'pointer',
            marginTop: 10,
          }}
        >
          Start Scanning
        </button>
      )}
      {scanning && <p>📷 Scanning in progress...</p>}

      {imageSrc && (
        <div style={{ marginTop: 20 }}>
          <h3>Scanned Document:</h3>
          <img
            src={imageSrc}
            alt="Scanned Document"
            style={{ width: '100%', maxWidth: 400, borderRadius: '8px', boxShadow: '0 2px 10px rgba(0,0,0,0.2)' }}
          />
        </div>
      )}
    </div>
  )
}

This component uses React’s useState hook to manage two pieces of component state: imageSrc stores the URL of the last scanned image for display, and scanning is a boolean flag that tracks when the native scanner is active. The core functionality is in the scanDocument function, which is triggered by a button click; this async function calls the DocumentScanner.scanDocument() method, and if successful, it processes the resulting image file path using Capacitor.convertFileSrc to make it renderable in the web view before updating imageSrc.

Now, import and use the component in your App.jsx file by replacing its contents with the following code:

import './App.css'
import DocumentScannerView from './components/DocumentScannerView'

function App() {
  return <DocumentScannerView />
}

export default App

Now build your app and sync the native projects.

npm run build
npx cap sync

Finally, run the app on Android and iOS using the following commands:

Android:

npx cap run android

iOS:

npx cap run ios
Scanning a document with our Capacitor ML Kit document scanning app

Common issues & fixes

IssueCauseFix
Camera permission not grantedMissing permissions in AndroidManifest.xml or Info.plistRefer to step 3 of this tutorial.
Signing for "App" requires a development teamXcode requires every iOS app to be signedOpen Xcode → App target → Signing & Capabilities → Select your Apple ID or development team
Plugin not foundCapacitor sync missingRun npx cap sync after installing new plugins
Failed Registering Bundle IdentifierThe chosen app identifier (e.g. com.documentscanner.app) is already taken on Apple’s developer registryOpen Xcode → App target → General → change Bundle Identifier to a unique string (e.g. com.yourname.documentscanner)

Conclusion

This concludes our tutorial on how to set up a simple document scanning app using Capacitor and ML Kit.

Free solutions like this one can be great for prototyping and personal projects. However, they have their drawbacks.

Since this plugin is a wrapper for Google’s ML Kit, its functionality depends entirely on this third-party library. Companies relying on ML Kit for their scanning needs won’t be able to submit feature requests nor count on help when things don’t work as expected.

We developed the Scanbot Document Scanner SDK to help companies overcome these hurdles. Our goal was to provide a developer-friendly solution for a wide range of platforms that consistently delivers high-quality results, even in challenging circumstances – enterprise-grade support included.

In the following tutorial, we’ll show you how to set up a powerful cross-platform document scanner using Capacitor, Ionic, and the Scanbot SDK.

Building an Ionic Capacitor document scanner app with the Scanbot SDK

To set up our app, we’ll follow these steps:

  1. Preparing the project
  2. Installing the SDK
  3. Initializing the SDK
  4. Implementing the scanning feature
  5. Implementing the PDF export feature

Thanks to the SDK’s Ready-to-Use UI Components, we’ll have an intuitive user interface out of the box.

Scanning a document and exporting it as a PDF using our Ionic Capacitor Document Scanner app

Developing Capacitor applications can be done via the CLI or using the VS Code extension. In this tutorial, we’ll use CLI commands. However, feel free to follow along using whatever option suits you best.

Capacitor can be used with different frameworks. In this tutorial, we will use Capacitor with Ionic and Angular. Again, these choices depend on your needs and prior knowledge.

Since we’ll use Ionic in this tutorial, you also need to install the Ionic CLI if you haven’t already:

npm install -g @ionic/cli

Step 1: Prepare the project

1. Create a new Capacitor project

To start creating your Capacitor app with Ionic using the CLI, run the following command in the terminal, which will create a blank project with some recommended Capacitor dependencies:

ionic start CapacitorTutorial blank --capacitor --type angular-standalone --package-id io.scanbot.tutorial.capacitor

⚠️ When using your own license, make sure that the package ID is the same as the application/bundle ID associated with it.

2. Generate the native projects

Now, let’s add the Android and iOS platforms to our project.

Navigate into the project directory.

cd CapacitorTutorial

Then add Android and iOS as platforms.

npm i @capacitor/android @capacitor/ios

And run the following commands to create the native Android and iOS projects:

npx cap add android
npx cap add ios

Step 2: Install file-viewer and the Document Scanner SDK

First, install file-viewer, the Capacitor plugin that we’ll use to open and preview our generated PDF files:

npm i @capacitor/file-viewer

Next, install the Capacitor Document Scanner SDK:

npm i capacitor-plugin-scanbot-sdk

💡 This will install the latest Scanbot SDK version. You can find more information about each version in the changelog.

Now that the npm package has been installed, we need to make some changes to the native projects.

For Android, we need to add the camera permission and feature in android/app/src/main/AndroidManifest.xml:

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

For iOS, we need to include a description for the camera permission in ios/App/App/Info.plist anywhere inside the <dict> element:

<key>NSCameraUsageDescription</key>
<string>Camera permission is needed to scan documents</string>

Now that the project is set up, we can start integrating the document scanning functionalities.

Step 3: Initialize the SDK

Before using any feature of the Scanbot SDK, we need to initialize it. Ideally, initialization should be done as soon as the app is launched.

In this tutorial, we’re going to initialize the SDK inside a ngOnInit callback in src/app/app.component.ts. Make sure to import OnInit and ScanbotSDK.

Your app.component.ts will look like this:

import { Component, OnInit } from '@angular/core';
import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';

import { ScanbotSDK } from 'capacitor-plugin-scanbot-sdk';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  imports: [IonApp, IonRouterOutlet],
})
export class AppComponent implements OnInit {
  constructor() { }

  ngOnInit(): void {
    ScanbotSDK.initializeSDK({
      licenseKey: ""
    }).then(result => console.log(result))
      .catch(err => console.log(err));
  }  
}

💡 Without a license key, our SDK only runs for 60 seconds per session. This is more than enough for the purposes of our tutorial, but if you like, you can generate a license key using the bundle and application identifiers.

Step 4: Implement the scanning feature

In your project folder, go to src/app/home/home.page.html, and add a button inside the container div that will start the scanning process.

<ion-button (click)="startDocumentScanner()">Start Document Scanner</ion-button>

We need to define the startDocumentScanner function inside the HomePage class in src/app/home/home.page.ts. Make sure that ScanbotSDK, DocumentScanningFlow and startDocumentScanner are imported and IonButton is added in the component imports.

In this tutorial, we use a default configuration object. It will start the Document Scanner UI with the default settings: in multi-page scanning mode with an acknowledge screen after scanning each page. You can customize the UI and behavior of the Document Scanner by modifying the configuration object. For more information on how to do that, please refer to the SDK’s RTU UI documentation.

Your home.page.ts should look something like this:

import { Component } from '@angular/core';
import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton } from '@ionic/angular/standalone';

import { ScanbotSDK } from 'capacitor-plugin-scanbot-sdk';
import { startDocumentScanner, DocumentScanningFlow } from 'capacitor-plugin-scanbot-sdk/ui_v2';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
  imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonButton],
})
export class HomePage {
  constructor() { }

  async startDocumentScanner() {
    try {
      /** Check license status and return early if the license is not valid */
      if (!(await ScanbotSDK.getLicenseInfo()).isLicenseValid) {
        return;
      }
      /**
       * Create the document configuration object and
       * start the document scanner with the configuration
       */
      const configuration = new DocumentScanningFlow();
      const documentResult = await startDocumentScanner(configuration);
      /**
       * Handle the result if the result status is OK
       */
      if (documentResult.status === 'OK') {
        /* Print out the documentImageURI for each page */
        documentResult.data.pages.forEach((page) => console.log(page.documentImageURI));
      }
    } catch (e: any) {
      console.log("An error has occurred while running Document Scanner", e.message);
    }
  }
}

Step 5: Implement the PDF export feature

Now, we will generate a PDF file from the scanned document.

Make sure to add FileViewer and PdfConfiguration to the imports in home.page.ts:

import { ScanbotSDK, PdfConfiguration } from 'capacitor-plugin-scanbot-sdk';
import { FileViewer } from '@capacitor/file-viewer';

To enable users to scan documents and generate a PDF, we need to modify the startDocumentScanner method. This method will first launch the document scanner, then process the scanned document to generate a PDF file, and finally open it.

async startDocumentScanner() {
  try {
    /** Check license status and return early if the license is not valid */
    if (!(await ScanbotSDK.getLicenseInfo()).isLicenseValid) {
      return;
    }
    /**
      * Create the document configuration object and
      * start the document scanner with the configuration
      */
    const configuration = new DocumentScanningFlow();
    const documentResult = await startDocumentScanner(configuration);
    /**
      * Handle the result if the result status is OK
      */
    if (documentResult.status === 'OK') {
      /* Print out the documentImageURI for each page */
      documentResult.data.pages.forEach((page) => console.log(page.documentImageURI));

      /* Create a PDF configuration object */
      const pdfConfiguration = new PdfConfiguration();

      /* Create a PDF file from the provided document UUID and the configuration object */
      const createPDFResult = await ScanbotSDK.Document.createPDF({
        documentID: documentResult.data.uuid,
        pdfConfiguration: pdfConfiguration
      });

      /* Open the created PDF file */
      await FileViewer.openDocumentFromLocalPath({
        path: createPDFResult.pdfFileUri
      });
    }
  } catch (e: any) {
    console.log("An error has occurred while running Document Scanner", e.message);
  }
}

Now you can open a scanned document as a PDF file and share it. For example, you can send it via email or save it to a cloud storage.

In the terminal, run these commands to build and sync the native projects:

npm run build
npx cap sync

To run the app on Android and iOS, use the following commands:

For Android:

npx cap run android

For iOS:

To run the app on a real iOS device, you need to adjust the “Provisioning” and “Signing” settings. Open ios/App/App.xcworkspace with Xcode, adjust the settings, and run the project from Xcode or with this command:

npx cap run ios
Scanning a document and exporting it as a PDF using our Ionic Capacitor Document Scanner app

Conclusion

And that’s it! You’ve successfully integrated a fully functional document scanner into your app 🎉

If this tutorial has piqued your interest in integrating document scanning functionalities into your Capacitor app, make sure to take a look at the other neat features in the Capacitor Document Scanner SDK’s documentation – or run our example project for a more hands-on experience.

Should you have questions about this tutorial or run into any issues, we’re happy to help! Just shoot us an email via tutorial-support@scanbot.io.

Happy scanning! 🤳

Related blog posts

Experience our demo apps

Barcode Icon Art

Barcode Scanner SDK

Scan 1D and 2D barcodes reliably in under 0.04s. Try features like Batch Scanning, Scan & Count, and our AR Overlays.

Launch Web Demo

Scan the code to launch the web demo on your phone.

Web QR Code

Also available to download from:

Document Icon Art

Document Scanner SDK

Scan documents quickly and accurately with our free demo app. Create crisp digital scans in seconds.

Launch Web Demo

Scan the code to launch the web demo on your phone.

Black and white QR code. Scan this code for quick access to information.

Also available to download from:

Data_capture Icon Art

Data Capture Modules

Try fast, accurate data capture with our demo app. Extract data from any document instantly – 100% secure.

Launch Web Demo

Scan the code to launch the web demo on your phone.

Black and white QR code. Scan this quick response code with your smartphone.

Also available to download from: