How to Build a Cross-Platform QR Code Scanner with Capacitor

Capacitor is an open source native runtime created by the Ionic team for building Web Native apps. We can use it to create cross-platform iOS, Android, and Progressive Web Apps with JavaScript, HTML, and CSS.1 As an alternative to Cordova, Capacitor delivers the same cross-platform benefits, but with a more modern approach to app development, taking advantage of the latest Web APIs and native platform capabilities.2

In this article, we are going to talk about how to use the capacitor barcode reader plugin to build a QR code scanner using Dynamsoft Barcode Reader (DBR) along with Dynamsoft Camera Enhancer (DCE).

A screenshot of a demo app running on Android:


Build a QR Code Scanner using Capacitor

Integrate Capacitor with an existing web app

Capacitor can be integrated with existing web apps to add native functionality.

Here, we create a react app first.

npx create-react-app qr-code-scanner

After the app is created, drop Capacitor into it.

cd qr-code-scanner
npm install @capacitor/cli @capacitor/core
npx cap init

Then, we can create projects for Android and iOS.

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

Use the following commands to update files after the app is changed:

npm run build
npx cap sync

Use plugins to bring QR code scanning functionality to the app

Plugins in Capacitor enable JavaScript to interface directly with Native APIs.3 A plugin has been created to make it easy to use Dynamsoft Barcode Reader and Dynamsoft Camera Enhancer in a Capacitor app to scan QR codes. Since Dynamsoft Barcode Reader has Android, iOS and JavaScript editions, the plugin also supports Android, iOS and Web. We can test the app on the web browsers first and then make modifications to make it compatible with native platforms.

  1. Install the plugin to the web app we just made

     npm install capacitor-plugin-dynamsoft-barcode-reader
  2. Initialize the plugin when the page is loaded. You may need to apply for a trial license to use DBR from here

     let [initialized,setInitialized] = useState(false);
     useEffect(() => {
       async function init() {
         let options = {"license": "license key"}
         let result = await DBR.initialize(options);
         if (result) {
           if (result.success == true) {
     }, []);
  3. Add a Start Scanning button in App.js to use the plugin. The button will be disabled if the plugin is not initialized

     +  import { DBR } from 'capacitor-plugin-dynamsoft-barcode-reader';
     +  async function startScan() {
     +    await DBR.startScan();
     +  }
       return (
         <div className="App">
           <header className="App-header">
             <img src={logo} className="App-logo" alt="logo" />
     +       <button id="scan" onClick={startScan} disabled={!initialized}>
     +           Start Scanning
     +       </button>

    Start the development server to see the results:

     npm run start
  4. Add a listener to receive the scan results

        await DBR.startScan();
     +  DBR.addListener('onFrameRead', (retObj) => {
     +    onFrameRead(retObj);
     +  });

    The onFrameRead function handles the results of one frame.

     import { Toast } from '@capacitor/toast';
     async function onFrameRead(retObj){
       let results = retObj["results"];
       var message = ""; 
       for (let index = 0; index < results.length; index++) {
         if (index>0){
           message = message + "\n";
         const result = results[index];
         message = message + result["barcodeFormat"]+": "+result["barcodeText"];
         text: message,

    The Toast plugin is used to make a toast message. It has to be installed via the following command:

     npm install @capacitor/toast
     npx cap sync

    PWA Elements is also needed to enable Toast on the web side, see the guide for more info.

  5. Add a Close button to stop the scan

    Added JSX content:

     <div id="controlContainer">
         <button className="scanner-control close" onClick={stopScan}>

    Added function:

     async function stopScan(){
         await DBR.stopScan();
  6. Add a Toggle Torch button to enable torch/flashlight

    Added JSX content:

     <button className="scanner-control toggleTorch" onClick={toggleTorch}>
         Toggle Torch

    Added function:

     var torchOn = false; //global variable
     async function toggleTorch(){
         let desiredState = true;
         if (torchOn){
           desiredState = false;
         await DBR.toggleTorch({"on":desiredState});
         torchOn = desiredState;

    Please note that on the web side, only Chrome 59+ on desktop and Android supports flashlight.

The CSS of the Toggle Torch and Close button:

    position: absolute;
    z-index: 999;

.toggleTorch {
    right: 0;
    bottom: 0;
    height: 25px;

.close {
    right: 0;
    bottom: 25px;
    height: 25px;

Native Mobile Quicks

On Android and iOS, the plugin uses a native view to show camera preview via Dynamsoft Camera Enhancer. There are several aspects to which we have to pay attention.

  1. Add camera permission

    Camera permission is required to use the camera.

    On Android, DCE handles this for us. We don’t need to do extra configurations.

    On iOS, we have to add the following to the Info.plist:

     <string>For barcode scanning</string>
     <string>For barcode scanning</string>
  2. Display WebView above the CameraView.

    Because the native CameraView is in parallel to the WebView, in order to show the CameraView while not blocking the web controls, we have to put the CameraView behind the WebView and make the WebView transparent.

    The plugin does this for us. We only need to set the background color of web elements to be transparent.

    In App.js, add the following code to hide the element with background and display the scanner controls.

     function showControlAndhideBackground(){
     function hideControlAndRevealBackground(){

    Use them in the startScan and stopScan functions:

     async function startScan() {
     + showControlAndhideBackground();
       await DBR.startScan();
       DBR.addListener('onFrameRead', (retObj) => {
     async function stopScan(){
         await DBR.stopScan();
     +   hideControlAndRevealBackground();

Now, we can build the web app and run it on Android or iOS devices.

For example, here is how to run the app on Android.

npm run build
npx cap open android

Android Studio will be used to open the project. We can debug and release the app with Android Studio.

Modify the runtime settings to scan QR code

Dynamsoft Barcode Reader can read more than 17 barcode formats (EAN13, UPCA, PDF417, QR code, etc). We can modify its settings to scan QR code only.

A JSON template can be used to update the runtime settings of DBR. The following template designates the barcode format to QR code:


We can use initRuntimeSettingsWithString to update the runtime settings with a template:

let template = "{\"ImageParameter\":{\"BarcodeFormatIds\":[\"BF_QR_CODE\"],\"Description\":\"\",\"Name\":\"Settings\"},\"Version\":\"3.0\"}";
await DBR.initRuntimeSettingsWithString({template: template})

You can learn more about DBR’s runtime settings here.

Ionic React Example

You can learn about how to use the plugin in React in a declarative way by checking out this article: Ionic React QR Code Scanner with Capacitor.

Source Code