Build a Nuxt 3 Barcode and QR Code Scanner with Webcam Access

Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.

Build a Nuxt.js 3 barcode and QR code scanner that opens a webcam stream and reads 1D/2D barcodes in real time using Dynamsoft Camera Enhancer for camera access and Dynamsoft Barcode Reader for decoding. Try the online demo to see the final result.

What you’ll build: A Nuxt.js web application that opens a webcam stream and scans barcodes and QR codes in real time using the Dynamsoft Barcode Reader SDK.

Key Takeaways

  • Dynamsoft Barcode Reader can be integrated into a Nuxt.js app as a Vue component that reads 1D/2D barcodes from a live webcam feed.
  • The CaptureVisionRouter processes camera frames and returns decoded barcode results via an event-driven callback.
  • The SDK’s WebAssembly engine runs entirely client-side, so no server-side barcode processing is required.
  • Wrapping the scanner component in <ClientOnly> is necessary because Nuxt uses server-side rendering by default.

Common Developer Questions

How do I scan QR codes from a webcam in a Nuxt.js app?

Initialize the license and preload DBR in dynamsoft.config.ts, then create CameraEnhancer and CaptureVisionRouter in a mounted barcode scanner component. Start camera capture with the ReadSingleBarcode preset and emit decoded results back to the parent page for display.

How do I create a reusable barcode scanner Vue component for Nuxt 3?

Encapsulate camera setup and decoding inside BarcodeScanner.vue, emit initialized and scanned events, and expose start or stop methods so parent pages can control scanning. In this guide, app.vue uses a component ref and a toggle button to pause or resume scanning while rendering result items in a list.

Why does my Dynamsoft Barcode Reader component fail during Nuxt server-side rendering?

The SDK depends on browser APIs like document and media device access, so it cannot run during server rendering. Wrap the scanner in ClientOnly and run initialization in onMounted so barcode logic executes only on the client.

Prerequisites

Step 1: Create a New Nuxt.js Project

Create a new Nuxt.js project named barcode-scanner:

npx nuxi@latest init barcode-scanner

Step 2: Install the Barcode Reader SDK

Install the Dynamsoft Barcode Reader bundle.

npm install dynamsoft-barcode-reader-bundle

Step 3: Configure the SDK License and Resources

Create a file named dynamsoft.config.ts with the following content to initialize the license and resources.

import { CoreModule } from "dynamsoft-core";
import { LicenseManager } from "dynamsoft-license";
import "dynamsoft-barcode-reader";

let initialized = false;
export async function init(){
  if (initialized) {
    return;
  }
  // Configures the paths where the .wasm files and other necessary resources for modules are located.
  CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/";

  /** LICENSE ALERT - README
   * To use the library, you need to first specify a license key using the API "initLicense()" as shown below.
   */

  await LicenseManager.initLicense("LICENSE-KEY", {
    executeNow: true,
  });

  /**
   * You can visit https://www.dynamsoft.com/customer/license/trialLicense?utm_source=samples&product=dbr&package=js to get your own trial license good for 30 days.
   * Note that if you downloaded this sample from Dynamsoft while logged in, the above license key may already be your own 30-day trial license.
   * For more information, see https://www.dynamsoft.com/barcode-reader/docs/web/programming/javascript/user-guide/index.html?ver=10.4.2002&cVer=true#specify-the-license&utm_source=samples or contact support@dynamsoft.com.
   * LICENSE ALERT - THE END
   */

  // Optional. Preload "BarcodeReader" module for reading barcodes. It will save time on the initial decoding by skipping the module loading.
  await CoreModule.loadWasm(["DBR"]);
  initialized = true;
  return;
}

Step 4: Create a Barcode Scanner Vue Component

Next, let’s create a barcode scanner component under src/components/BarcodeScanner.vue that can open the camera and scan barcodes from the camera video frames.

  1. Write the basic content of the component:

    <script setup lang="ts">
    const cameraViewContainer: Ref<HTMLElement | null> = ref(null);
    </script>
    
    <template>
      <div ref="cameraViewContainer" id="cameraViewContainer"></div>
    </template>
    
    <style scoped>
    #cameraViewContainer {
      width: 100%;
      height: 100%;
      top: 0;
      left: 0;
      position: absolute;
    }
    </style>
    

    It has an element which serves as the container of the camera.

  2. Run the init method defined in dynamsoft.config.ts after the component is mounted.

    onMounted(async () => {
      await init();
    });
    
  3. Initialize Camera Enhancer and bind it to the UI element after the component is mounted.

    const cameraViewContainer: Ref<HTMLElement | null> = ref(null);
    let cameraEnhancer: CameraEnhancer;
    let cameraView:CameraView;
    onMounted(async () => {
      try {
        await init();
        // Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control.
        cameraView = await CameraView.createInstance();
        cameraEnhancer = await CameraEnhancer.createInstance(cameraView);
    
        // Get default UI and append it to DOM.
        cameraViewContainer.value!.append(cameraView.getUIElement());
      } catch (ex: any) {
        let errMsg = ex.message || ex;
        console.error(errMsg);
      }
    });
    
  4. Create a Capture Vision Router instance to process the camera frames to read barcodes.

    let cvRouter: CaptureVisionRouter;
    onMounted(async () => {
      try {
        //...
        cvRouter = await CaptureVisionRouter.createInstance();
        cvRouter.setInput(cameraEnhancer);
      } catch (ex: any) {
        let errMsg = ex.message || ex;
        console.error(errMsg);
      }
    });
    
  5. Define two emit events. One is to emit the detected barcode results and the other is to emit the initialized event.

    // define a 'scanned' event that the Scanner component emits when frames are scanned
    const emit = defineEmits<{
      (e: 'scanned', results: BarcodeResultItem[]): void
      (e: 'initialized'): void
    }>();
    
    1. Add a result receiver to get the detected barcode results and emit them.

      // Define a callback for results.
      cvRouter.addResultReceiver({
        onDecodedBarcodesReceived: (result) => {
          emit("scanned",result.barcodeResultItems);
        }
      });
      
    2. Emit initialized after the instances are created.

      onMounted(async () => {
        try {
          //...
          emit("initialized");
        } catch (ex: any) {
          let errMsg = ex.message || ex;
          console.error(errMsg);
        }
      });
      
  6. Expose two methods to control the scanning status of the component.

    // expose start/stop to control pause/resume scanning
    const start = async () => await startScanning();
    const stop = () => stopScanning();
    
    defineExpose({ start, stop });
       
    const stopScanning = () => {
      cameraView?.setScanLaserVisible(false);
      cvRouter?.stopCapturing();
      cameraEnhancer.close();
    }
    
    const startScanning = async () => {
      await cameraEnhancer.open();
      cvRouter?.startCapturing("ReadSingleBarcode");
      cameraView?.setScanLaserVisible(true);
    }
    
  7. Release resources before the component is unmounted.

    // dispose cvRouter when it's no longer needed
    onBeforeUnmount(async () => {
      try {
        cvRouter?.dispose();
        cameraEnhancer?.dispose();
      } catch (_) { }
    });
    

Step 5: Integrate the Scanner Component into Your Nuxt App

Switch to app.vue. Let’s use the scanner component in the app.

  1. Import the scanner component and add a button to control its scanning status. Since the SDK has to modify the DOM, we need to wrap the scanner with ClientOnly.

    <template>
      <div id="app">
        <h2>Barcode Scanner in Nuxt.js</h2>
        <button v-if="initialized" @click="toggleScanning"></button>
        <span v-if="!initialized">Initializing...</span>
        <div class="container" v-if="mounted">
          <ClientOnly fallback-tag="span" fallback="Loading barcode scanner...">
            <BarcodeScanner ref="scanner" @initialized="onInitialized" @scanned="onScanned"></BarcodeScanner>
          </ClientOnly>
        </div>
    </template>
    <script setup lang="ts">
    import type { BarcodeResultItem } from 'dynamsoft-barcode-reader-bundle';
    import BarcodeScanner from './components/BarcodeScanner.vue';
    const scanner = ref();
    const initialized = ref(false);
    const scanning = ref(false);
    const onInitialized = () => {
      initialized.value = true;
    }
    const toggleScanning = () => {
      if (scanner.value) {
        scanning.value = !scanning.value;
        if (scanning.value) {
          scanner.value.start();
        }else{
          scanner.value.stop();
        }
      }else{
        alert("Not mounted");
      }
    }
    </script>
    <style lang="css" scoped>
    .container {
      position: relative;
      width: 360px;
      height: 360px;
    }
    </style>
    
  2. Display the scanned results.

    <template>
      <div>
        <div>
          Results:
        </div>
        <ol>
          <li v-for="(barcode,index) in scannedBarcodes" :key="'barcode-'+index"></li>
        </ol>
      </div>
    </template>
    <script setup lang="ts">
    const scannedBarcodes = ref<BarcodeResultItem[]>([]);
    const onScanned = (barcodes:BarcodeResultItem[]) => {
      if (barcodes.length>0) {
        scannedBarcodes.value = barcodes;
      }
    }
    </script>
    

All right, we’ve now finished the barcode and QR code scanner with Nuxt.js. You can check out the online demo to have a try.

Common Issues and Edge Cases

  • Camera permission denied: If the user blocks camera access, CameraEnhancer.open() will throw an error. Wrap the call in a try/catch and display a user-friendly message prompting them to allow camera permissions in their browser settings.
  • SSR hydration mismatch: Dynamsoft SDK accesses browser-only APIs (navigator.mediaDevices, document). Always wrap <BarcodeScanner> in <ClientOnly> to prevent server-side rendering errors in Nuxt.
  • Slow initial load on first scan: The SDK loads WebAssembly modules on first use. Call CoreModule.loadWasm(["DBR"]) during initialization (as shown in Step 3) to preload the module and avoid a delay when the user starts scanning.

Frequently Asked Questions

How do I implement a QR code reader in Nuxt.js?

Install dynamsoft-barcode-reader-bundle, create a Vue component that initializes CameraEnhancer and CaptureVisionRouter, and wrap it in <ClientOnly> to avoid SSR issues. This tutorial walks through every step.

Does the Dynamsoft Barcode Reader work with Nuxt 3 server-side rendering?

The SDK itself requires browser APIs and cannot run on the server. Use Nuxt’s <ClientOnly> wrapper so the scanner component only renders on the client side.

What barcode formats can this Nuxt.js scanner read?

Dynamsoft Barcode Reader supports over 20 barcode formats including QR Code, Data Matrix, PDF417, Code 128, Code 39, EAN-13, UPC-A, and more. The ReadSingleBarcode preset used in this tutorial auto-detects the format.

Source Code

Get the complete sample project source code on GitHub