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

@servicemind.tis/angular-qr-code-scanner

v1.0.9

Published

Lightweight Angular library for scanning QR codes using the device camera, with iOS recovery, torch control, and continuous-scan mode.

Readme

@servicemind.tis/angular-qr-code-scanner

npm version npm downloads CI License: MIT Angular

A lightweight Angular library for scanning QR codes using the device camera. Drop in one component and get instant QR scanning with iOS recovery, torch control, back-camera auto-selection, and continuous-scan mode — all backed by Angular Material UI.

Built on top of ngx-scanner-qrcode.


Table of Contents


Features

  • One-component drop-in<angular-qr-code-scanner> handles everything
  • Single-shot and continuous scan modes with a built-in toggle
  • iOS Safari black-frame recovery — auto-recreates the <video> element on first load
  • Torch (flashlight) toggle where the browser/device supports it
  • Automatic back-camera selection across multi-camera devices
  • Camera-switch button to cycle through available cameras
  • Programmatic resume — pass a new config with playPause: true to restart after a pause
  • Angular Material UI — no extra CSS framework required
  • Strict TypeScript with exported interfaces for full type safety

Requirements

| Dependency | Version | | ------------------- | -------------------- | | Angular | 19+ | | @angular/material | 19+ | | @angular/cdk | 19+ | | ngx-scanner-qrcode | ^1.7.6 | | Browser | Modern browser with getUserMedia support |


Installation

npm install @servicemind.tis/angular-qr-code-scanner

Install peer dependencies if not already present:

npm install @angular/material @angular/cdk ngx-scanner-qrcode

Quick Start

1. Import the module

// app.module.ts (NgModule)
import { AngularQrCodeScannerModule } from '@servicemind.tis/angular-qr-code-scanner';

@NgModule({
  imports: [AngularQrCodeScannerModule]
})
export class AppModule {}

2. Add the component to your template

<angular-qr-code-scanner
  [config]="config"
  (onScanned)="onScanned($event)"
  (onChangeMode)="onChangeMode($event)">
</angular-qr-code-scanner>

3. Wire up the component

import { AngularQrCodeScannerConfig } from '@servicemind.tis/angular-qr-code-scanner';

export class AppComponent {
  config: AngularQrCodeScannerConfig = {
    isPauseAfterScan: true,
    isEnableMode: true,
    isValidate: false,
    isContinuousMode: false,
  };

  onScanned(result: string): void {
    console.log('QR value:', result);
  }

  onChangeMode(isContinuousMode: boolean): void {
    this.config = { ...this.config, isContinuousMode };
  }
}

Usage in a Standalone Component

import { Component } from '@angular/core';
import { AngularQrCodeScannerModule, AngularQrCodeScannerConfig } from '@servicemind.tis/angular-qr-code-scanner';

@Component({
  selector: 'app-scanner',
  standalone: true,
  imports: [AngularQrCodeScannerModule],
  template: `
    <angular-qr-code-scanner
      [config]="config"
      (onScanned)="onScanned($event)"
      (onChangeMode)="onChangeMode($event)">
    </angular-qr-code-scanner>

    <p *ngIf="result">Last scan: {{ result }}</p>
  `
})
export class ScannerComponent {
  result = '';

  config: AngularQrCodeScannerConfig = {
    isPauseAfterScan: true,
    isEnableMode: true,
    isContinuousMode: false,
  };

  onScanned(value: string): void {
    this.result = value;
  }

  onChangeMode(isContinuousMode: boolean): void {
    this.config = { ...this.config, isContinuousMode };
  }
}

Usage inside a Material Dialog

Open the scanner inside a MatDialog and close it with the decoded value:

Dialog component:

import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AngularQrCodeScannerModule, AngularQrCodeScannerConfig } from '@servicemind.tis/angular-qr-code-scanner';

export interface ScanDialogData {
  label?: string;
  config?: AngularQrCodeScannerConfig;
}

@Component({
  selector: 'app-scan-dialog',
  standalone: true,
  imports: [AngularQrCodeScannerModule],
  template: `
    <h2 mat-dialog-title>{{ data.label ?? 'Scan QR Code' }}</h2>
    <mat-dialog-content>
      <angular-qr-code-scanner
        [config]="config"
        (onScanned)="onScanned($event)">
      </angular-qr-code-scanner>
    </mat-dialog-content>
  `
})
export class ScanDialogComponent {
  config: AngularQrCodeScannerConfig;

  constructor(
    private dialogRef: MatDialogRef<ScanDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data: ScanDialogData
  ) {
    this.config = data.config ?? { isPauseAfterScan: true, isEnableMode: true };
  }

  onScanned(result: string): void {
    this.dialogRef.close(result);
  }
}

Opener:

import { MatDialog } from '@angular/material/dialog';
import { ScanDialogComponent } from './scan-dialog.component';

@Component({ ... })
export class AppComponent {
  constructor(private dialog: MatDialog) {}

  openScanner(): void {
    this.dialog.open(ScanDialogComponent, {
      width: '400px',
      data: { label: 'Scan your ticket', config: { isPauseAfterScan: true } },
    }).afterClosed().subscribe((result?: string) => {
      if (result) console.log('Scanned:', result);
    });
  }
}

API Reference

Inputs

| Input | Type | Required | Description | | -------- | ---------------------------- | -------- | --------------------------------- | | config | AngularQrCodeScannerConfig | Yes | Scanner behaviour configuration. |

Outputs

| Output | Payload type | Description | | -------------- | ------------ | ------------------------------------------------------------ | | onScanned | string | Emits the decoded QR string on every successful scan. | | onChangeMode | boolean | Emits the new isContinuousMode state when the user toggles.|


Config Options

export interface AngularQrCodeScannerConfig {
  isPauseAfterScan: boolean;  // Pause automatically after a successful scan. Default: true
  isEnableMode: boolean;      // Show the single-shot / continuous mode toggle. Default: true
  isValidate?: boolean;       // Validate the payload before emitting. Default: false
  isContinuousMode?: boolean; // Keep scanning without manual resume. Default: false
  playPause?: boolean;        // Pass true in a new config object to programmatically resume.
}

| Property | Type | Default | Description | | ------------------ | --------- | ------- | -------------------------------------------------------------------------------- | | isPauseAfterScan | boolean | true | Freeze the camera preview after a successful scan so the user can read the code. | | isEnableMode | boolean | true | Show the toggle button that switches between single-shot and continuous modes. | | isValidate | boolean | false | Run additional payload validation before emitting via onScanned. | | isContinuousMode | boolean | false | Keep scanning automatically without requiring a manual resume. | | playPause | boolean | false | Set to true in a new object reference to programmatically resume scanning. |


Programmatic Resume

After a scan pauses the camera, pass a new config object with playPause: true to resume without user interaction:

onScanned(result: string): void {
  this.result = result;
  doSomethingWith(result).then(() => {
    // Resume scanning
    this.config = { ...this.config, playPause: true };
  });
}

Important: always spread into a new object — the component uses object-reference change detection to detect the trigger.


Camera Permissions

The browser requests camera access when the scanner initialises. Camera access requires a secure context:

  • Production: https://your-domain.com
  • Local dev: http://localhost (treated as secure by browsers)

On iOS Safari, the component automatically:

  1. Sets muted and playsinline on the underlying <video> element
  2. Recreates the element once to work around the first-load black-frame issue

Browser Support

| Browser | Supported | | --------------- | --------- | | Chrome (desktop) | ✅ | | Edge | ✅ | | Firefox | ✅ | | Safari (macOS) | ✅ | | Chrome (Android) | ✅ | | Safari (iOS) | ✅ |

Camera access is gated on https in all modern browsers (except localhost).


Styling

The scanner fills the width of its container. Constrain it with a wrapper:

angular-qr-code-scanner {
  display: block;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
}

The component uses Angular Material internally; make sure a Material theme is loaded in your application (e.g., @angular/material/prebuilt-themes/azure-blue.css).


Troubleshooting

Camera not opening

  • Check that the user has granted camera permission in the browser.
  • Confirm the page is served over https in production.
  • Ensure no other tab or app is holding the camera.

Black screen on iOS Safari

The library handles this automatically via a one-time <video> element recreation. If you still see a black frame, confirm you are on iOS 14.3+ (minimum for getUserMedia on iOS Safari).

QR code not detected

  • Make sure the code is well-lit and fully in frame.
  • Try the camera-switch button if the front camera is selected by default.
  • Increase ambient light or reduce camera distance.

Tests fail in CI

Karma tests need a real or headless Chrome. In GitHub Actions the runner includes Chrome; run tests with --browsers=ChromeHeadless.


Contributing

Contributions, bug reports, and feature requests are welcome.

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Commit your changes using Conventional Commits
  4. Push the branch and open a Pull Request

Please open an issue before starting work on large changes so we can align on direction.


Changelog

See GitHub Releases for the full change history.


License

MIT © Thai Informatics System