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

cordova-plugin-qrscanner-pro

v1.0.0

Published

Highly customizable QR scanner plugin for Apache Cordova (Android & iOS).

Readme

Cordova QR Scanner Pro

Minimal, production-ready, highly customizable QR scanner plugin for Apache Cordova on Android and iOS.

Package id: cordova-plugin-qrscanner-pro
JS namespace: cordova.plugins.qrScannerPro

Table of Contents

UI Preview

Why this plugin

  • Native camera scanning on both platforms
  • Fast, stable QR detection with clean UX defaults
  • Full-screen customizable scanner UI (scan area, frame, overlay, loader, scan line, header, buttons)
  • Straightforward API for app flows (scan, cancel, toggleFlash, setFlash)
  • Security-friendly design (no network calls, local-only processing)

Installation

From npm (recommended)

cordova plugin add cordova-plugin-qrscanner-pro

From Git

cordova plugin add https://github.com/pwndex/cordova-qrscanner-pro.git

From local path

cordova plugin add /absolute/path/to/cordova-plugin-qrscanner-pro

Platform support

| Platform | Supported | Notes | |---|---|---| | Android | Yes | Uses ZXing (JourneyApps embedded) | | iOS | Yes | Uses AVFoundation |

Quick start

document.addEventListener("deviceready", function () {
  const qr = cordova.plugins.qrScannerPro;

  qr.scan(
    {
      restrictScanToZone: true,
      scanZoneWidth: 280,
      scanZoneHeight: 280,
      darkenOutside: true,
      frameColor: "#00E5FF",
      loaderColor: "#00E5FF",
      loadingDelayMs: 850,
      showFlashButton: true,
      showCancelButton: true,
      headerText: "Align QR in frame",
      headerHeight: 58,
      headerPadding: 12,
      safeAreaColor: "#101214",
      headerBackgroundColor: "#66000000",
      headerTextColor: "#FFFFFF",
      headerFontSize: 17,
      buttonMode: "icon",
      buttonSize: 54,
      buttonHeight: 54,
      buttonWidth: 120,
      buttonContentSize: 20,
      buttonCornerRadius: 27,
      buttonSpacing: 14,
      buttonBottomOffset: 56,
      flashButtonIcon:
        "<svg viewBox='0 0 24 24'><path d='M13 2L5 14h6l-1 8 9-13h-6l0-7z'/></svg>",
      cancelButtonIcon:
        "<svg viewBox='0 0 24 24'><path d='M6 6l12 12M18 6L6 18' stroke='currentColor' stroke-width='2.2' fill='none' stroke-linecap='round'/></svg>",
      buttonTextColor: "#FFFFFF",
      buttonBackgroundColor: "#80111111",
      flashButtonBackgroundColor: "#AA0057FF",
      flashButtonTextColor: "#FFFFFF",
      flashButtonActiveTextColor: "#111111",
      flashButtonActiveBackgroundColor: "#FF2F7BFF",
      cancelButtonBackgroundColor: "#AA2A2A2A",
      cancelButtonTextColor: "#FFFFFF",
      animateScanLine: true
    },
    function onSuccess(result) {
      // result = { text, format, cancelled, rawBytesBase64? }
      console.log("QR:", result.text, result.format);
    },
    function onError(message) {
      // Called on cancel, permission denial, or runtime errors
      console.warn("Scan finished with error:", message);
    }
  );
});

Platform-focused examples

Even though the JS API is the same on both platforms, production UX often differs by platform conventions.

Android example (larger scan zone + explicit formats)

document.addEventListener("deviceready", function () {
  const qr = cordova.plugins.qrScannerPro;

  function startAndroidScan() {
    qr.scan(
      {
        // Android-friendly defaults for mixed camera quality devices
        scanZoneWidth: 300,
        scanZoneHeight: 300,
        scanZoneOffsetY: -12,
        darkenOutside: true,
        overlayColor: "#99000000",

        frameColor: "#22D3EE",
        frameThickness: 4,
        frameCornerRadius: 14,

        showLoader: true,
        loaderColor: "#22D3EE",
        loadingDelayMs: 900,

        showFlashButton: true,
        showCancelButton: true,
        buttonMode: "icon",
        buttonSize: 54,
        buttonContentSize: 20,
        buttonCornerRadius: 27,
        buttonSpacing: 14,
        buttonBottomOffset: 56,
        flashButtonIcon: "⚡",
        cancelButtonIcon: "✕",

        animateScanLine: true,
        scanLineColor: "#22D3EE",
        scanLineThickness: 3,
        scanLineDurationMs: 1700,

        hapticFeedback: true,
        returnRawBytesBase64: true,

        // Android only: helps constrain decoding and improve speed
        formats: ["QR_CODE", "AZTEC", "DATA_MATRIX"]
      },
      function onSuccess(result) {
        console.log("[Android] text:", result.text);
        console.log("[Android] format:", result.format);
        if (result.rawBytesBase64) {
          console.log("[Android] raw bytes present");
        }
      },
      function onError(message) {
        // Includes user cancel and runtime errors
        console.warn("[Android] scan ended:", message);
      }
    );
  }

  document.getElementById("scanBtn").addEventListener("click", startAndroidScan);
});

iOS example (clean UI + strong cancel flow)

document.addEventListener("deviceready", function () {
  const qr = cordova.plugins.qrScannerPro;

  function startIOSScan() {
    qr.scan(
      {
        // iOS-like centered clean layout
        scanZoneWidth: 260,
        scanZoneHeight: 260,
        scanZoneOffsetY: 0,
        darkenOutside: true,
        overlayColor: "#88000000",

        frameColor: "#34D399",
        frameThickness: 3,
        frameCornerRadius: 12,

        showLoader: true,
        loaderColor: "#34D399",
        loadingDelayMs: 750,

        showFlashButton: true,
        showCancelButton: true,
        buttonMode: "icon",
        buttonSize: 54,
        buttonContentSize: 20,
        buttonCornerRadius: 27,
        buttonSpacing: 14,
        buttonBottomOffset: 56,
        flashButtonIcon: "⚡",
        cancelButtonIcon: "✕",

        buttonTextColor: "#FFFFFF",
        buttonBackgroundColor: "#A61E1E1E",

        animateScanLine: true,
        scanLineColor: "#34D399",
        scanLineThickness: 3,
        scanLineDurationMs: 1800,

        hapticFeedback: true
      },
      function onSuccess(result) {
        console.log("[iOS] scanned:", result.text);
      },
      function onError(message) {
        // Treat cancel as non-fatal branch
        if ((message || "").toLowerCase().includes("cancel")) {
          console.log("[iOS] user cancelled");
          return;
        }
        console.error("[iOS] scanning failed:", message);
      }
    );
  }

  document.getElementById("scanBtn").addEventListener("click", startIOSScan);
});

Safe URL handling example (both platforms)

function handleScannedText(text) {
  // Never trust scanned input directly
  let url;
  try {
    url = new URL(text);
  } catch (_) {
    console.log("Not a URL, treat as plain text:", text);
    return;
  }

  // Allowlist domains before opening externally
  const allowedHosts = new Set(["example.com", "app.example.com"]);
  if (!allowedHosts.has(url.hostname)) {
    console.warn("Blocked untrusted URL:", url.href);
    return;
  }

  // Open only trusted URLs
  cordova.InAppBrowser.open(url.href, "_system");
}

Flash control example (during active session)

const qr = cordova.plugins.qrScannerPro;

function enableFlashIfAvailable() {
  qr.isFlashAvailable(
    function (info) {
      if (info && info.available) {
        qr.setFlash(true, function () {
          console.log("Flash enabled");
        }, console.warn);
      }
    },
    console.warn
  );
}

API reference

scan(options, onSuccess, onError)

Starts a scanner session.

  • options: object with scanner customization
  • onSuccess(result): called when a code is successfully parsed
  • onError(message): called on cancel/error/permission denial

cancel(onSuccess, onError)

Cancels the active scanner session if present.

toggleFlash(onSuccess, onError)

Toggles device torch during active session.

  • success payload: { enabled: boolean }

setFlash(enabled, onSuccess, onError)

Explicitly sets torch state during active session.

  • enabled: boolean
  • success payload: { enabled: boolean }

isFlashAvailable(onSuccess, onError)

Checks torch availability on the current device.

  • success payload: { available: boolean }

Result object

Successful scan callback returns:

type ScanResult = {
  text: string;              // Decoded QR content
  format: string;            // QR_CODE | AZTEC | DATA_MATRIX | ...
  cancelled: false;
  rawBytesBase64?: string;   // Android only, if returnRawBytesBase64=true
};

Configuration options

All options are optional. Defaults are safe for production.

Scan Zone and Detection

| Option | Type | Default | Description | |---|---|---|---| | scanZoneWidth | number | 260 | Scan area width (dp/pt) | | scanZoneHeight | number | 260 | Scan area height (dp/pt) | | scanZoneOffsetY | number | 0 | Vertical offset for scan area | | restrictScanToZone | boolean | false | Accept detection only when code center is inside scan zone | | preferFrontCamera | boolean | false | Prefer front camera on iOS (if available) | | formats | string[] | QR-focused defaults | Custom ZXing formats (Android only) |

Overlay, Frame, and Scan Line

| Option | Type | Default | Description | |---|---|---|---| | darkenOutside | boolean | true | Darken area outside scan zone | | overlayColor | string | #88000000 | Overlay color | | frameColor | string | #00E676 | Scan frame color | | frameThickness | number | 3 | Frame stroke thickness | | frameCornerRadius | number | 12 | Frame corner radius | | animateScanLine | boolean | true | Enable animated scan line | | scanLineColor | string | #00E676 | Scan line color | | scanLineThickness | number | 3 | Scan line thickness | | scanLineDurationMs | number | 1800 | Full up/down animation duration |

Header

| Option | Type | Default | Description | |---|---|---|---| | headerText | string | "" | Top centered header text (empty = hidden) | | headerHeight | number | 56 | Header content height (dp/pt), rendered below top safe area | | headerPadding | number | 12 | Inner text padding inside header (dp/pt) | | safeAreaColor | string | "" | Top safe-area overlay color. Empty keeps app/theme default background | | headerBackgroundColor | string | #00000000 | Header background color only (safe area keeps app/default background) | | headerTextColor | string | #FFFFFFFF | Header text color | | headerFontSize | number | 18 | Header text size |

Buttons: Layout and Content

| Option | Type | Default | Description | |---|---|---|---| | showFlashButton | boolean | true | Show Flash button | | showCancelButton | boolean | true | Show Cancel button | | buttonMode | string | text | text or icon | | buttonSize | number | 52 | Base size for icon mode | | buttonWidth | number | 110 | Text mode button width | | buttonHeight | number | 52 | Button height in both modes | | buttonContentSize | number | 22 | Text/emoji/SVG icon size in button content | | buttonCornerRadius | number | 10 | Button corner radius | | buttonSpacing | number | 16 | Space between flash/cancel buttons | | buttonBottomOffset | number | 46 | Distance from bottom edge | | flashButtonText | string | Flash | Flash button label | | flashButtonIcon | string | | Icon content in buttonMode: "icon" (emoji/text/inline SVG/data SVG URL) | | cancelButtonText | string | Cancel | Cancel button label | | cancelButtonIcon | string | | Icon content in buttonMode: "icon" (emoji/text/inline SVG/data SVG URL) |

Buttons: Colors (Global + Per-Button Overrides)

| Option | Type | Default | Description | |---|---|---|---| | buttonTextColor | string | #FFFFFFFF | Button text/icon color (normal) | | buttonBackgroundColor | string | #66000000 | Button background color (normal) | | flashButtonTextColor | string | "" | Flash text/icon color override (normal) | | flashButtonBackgroundColor | string | "" | Flash background override (normal) | | flashButtonActiveTextColor | string | "" | Flash text/icon color override (active) | | flashButtonActiveBackgroundColor | string | "" | Flash background override (active) | | cancelButtonTextColor | string | "" | Cancel text/icon color override (normal) | | cancelButtonBackgroundColor | string | "" | Cancel background override (normal) |

active state behavior:

  • Flash button: active while pressed and while torch is ON.
  • Cancel button: uses only normal colors.
  • Flash active colors are persisted through toggle state.

Session Behavior and Feedback

| Option | Type | Default | Description | |---|---|---|---| | showLoader | boolean | true | Show loader before returning result | | loaderColor | string | #00E676 | Loader color | | loadingDelayMs | number | 700 | Delay before success callback | | hapticFeedback | boolean | true | Vibrate/beep on success |

Debug and Platform-Specific

| Option | Type | Default | Description | |---|---|---|---| | debug | boolean | false | Enable native debug logs for scan lifecycle | | debugTag | string | QrScannerPro | Prefix tag for native logs | | returnRawBytesBase64 | boolean | false | Return raw bytes (Android only) |

Debug mode

Use debug mode when scanner lifecycle events need troubleshooting.

cordova.plugins.qrScannerPro.scan(
  {
    debug: true,
    debugTag: "QrScannerPro",
    showCancelButton: true,
    showFlashButton: true
  },
  onSuccess,
  onError
);

Native logs will include events for:

  • scanner open/present
  • camera permission status
  • detection callback
  • flash toggle/set state
  • cancel button / cancel from JS
  • scanner close with success/error

Header + icon buttons

{
  headerText: "Scan payment QR",
  headerHeight: 60,
  headerPadding: 12,
  safeAreaColor: "#101214",
  headerBackgroundColor: "#66000000",
  headerTextColor: "#FFFFFF",
  headerFontSize: 18,

  frameCornerRadius: 14,
  overlayColor: "#CC000000",
  buttonMode: "icon",
  buttonSize: 54,
  buttonHeight: 54,
  buttonWidth: 120,
  buttonContentSize: 20,
  buttonCornerRadius: 27,
  buttonSpacing: 14,
  buttonBottomOffset: 56,

  // Base colors (used by both buttons by default)
  buttonTextColor: "#F8FAFC",
  buttonBackgroundColor: "#A61E1E1E",

  // Flash normal + active
  flashButtonTextColor: "#F8FAFC",
  flashButtonBackgroundColor: "#AA0EA5E9",
  flashButtonActiveTextColor: "#0B1220",
  flashButtonActiveBackgroundColor: "#FF38BDF8",

  // Cancel normal
  cancelButtonTextColor: "#FFFFFF",
  cancelButtonBackgroundColor: "#AA374151",

  // Icon value can be emoji/text or inline SVG / data:image/svg+xml,...
  flashButtonIcon: "<svg viewBox='0 0 24 24'><path d='M13 2L5 14h6l-1 8 9-13h-6l0-7z'/></svg>",
  cancelButtonIcon: "<svg viewBox='0 0 24 24'><path d='M6 6l12 12M18 6L6 18' stroke='currentColor' stroke-width='2' fill='none'/></svg>"
}

Header font uses Satoshi when available on device; otherwise it falls back to system semibold font.

Production recommendations

  • Keep loadingDelayMs between 600-1200 ms for smoother UX
  • Use larger scan area for dense or damaged QR codes
  • Handle permission denial with clear UI fallback in your app
  • Validate QR payload before navigation or API calls
  • Add app-level rate limiting if users can scan repeatedly

Security notes

  • No remote calls are made by the plugin
  • Scanning is fully local on device
  • Treat scanned data as untrusted input
  • Use allowlists for URL schemes/domains before opening links

Troubleshooting

Camera permission denied

  • Ensure camera permission is requested by OS
  • Verify NSCameraUsageDescription is present in iOS app plist
  • Ask user to re-enable permission in system settings

Flash button does nothing

  • Not all devices have torch hardware
  • Call isFlashAvailable() before enabling flash UX

Cancel returns error callback

  • This is expected behavior in current API design
  • Handle cancel as a non-fatal branch in onError

Development and release checklist

  • Verify scanning on at least 2 physical Android devices
  • Verify scanning on at least 2 physical iOS devices
  • Test low light, glare, small QR, and rotated QR cases
  • Test permission denied and permission revoked flows
  • Pin dependency versions before public release
  • Tag release and keep CHANGELOG.md per version

License

MIT