Skip to content

Building a ZXing React Native barcode scanner – tutorial

In this tutorial, we’ll walk you through creating a barcode scanner app in React Native using a port of the ZXing library.

Kevin December 16, 2024 14 mins read
app store

The ZXing library has been widely adopted for implementing barcode scanning functionalities, with several other libraries using it as the base. However, it’s an older project and in maintenance mode.

At the time of writing, the rn-barcode-zxing library is the only viable option for implementing a ZXing-based barcode scanner in a React Native app. So that’s what we’ll use in this tutorial.

To create our React Native barcode scanning app with ZXing, we’ll follow these steps:

  1. Creating a new React Native project
  2. Installing the ZXing React Native library and its dependencies
  3. Configuring the camera permissions
  4. Implementing the ScanCode component
  5. Creating a function to handle scanning
  6. Testing the app

Prerequisites

Before you start creating the project, make sure your development environment is set up with the following tools:

  • Node.js: Required to manage your React Native project. You can download it from the Node.js website if you don’t have it.
  • Android Studio: This IDE is necessary for Android development, as it includes the SDKs and emulators needed to build and test your app. Download the latest version from Android Studio.
  • Java Development Kit (JDK): React Native requires Java for Android builds. You can download the JDK from Oracle or use Microsoft’s OpenJDK.

Ensure these tools are installed and configured before continuing with the barcode scanner tutorial.

Step 1: Create a new React Native project

As your first step, you must create a new React Native project. Open your terminal and run the following command to create a new project named BarcodeScannerApp:

npx @react-native-community/cli init BarcodeScannerApp 

After creating the project, navigate to your project:

cd BarcodeScannerApp 

Step 2: Install the ZXing React Native library and its dependencies

To add camera capabilities to the React Native app, we will use the rn-barcode-zxing library. You can install it and its dependencies by running the following command in your project directory:

npm install rn-barcode-zxing react-native-vision-camera lottie-react-native

Step 3: Configure the camera permissions

Android

To allow the app to access the camera, you need to update android/app/src/main/AndroidManifest.xml. Add the following permission tags to the <manifest> tag:

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

iOS

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

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

Step 4: Implement the ScanCode component

Now that you’ve configured the camera permissions, the next step is to implement the library’s ScanCode component into the app.

Open your App.tsx file and include the follwing code:

import { ScanCode } from "rn-barcode-zxing";

const App = () => {
  return (
    <SafeAreaView style={[{ flex: 1 }]}>
      <View style={{ flex: 1 }}>
        <ScanCode shouldScan={shouldScan} onScanBarcode={onScanBarcode} />
      </View>
    </SafeAreaView>
  );
};

export default App;

This renders the ScanCode component, which will open the device’s back camera to scan for codes. This component requires onScanBarcode and shouldScan props to handle the scanned codes properly.

Step 5: Create a function to handle scanning

You need to handle the scanning in the ScanCode component by passing it a function in the onScanBarcode prop. The code below will read the scanned code and show an alert with the code’s value on the screen.

First, create a state to handle when the scanner should scan for codes. This will stop the scanning after the first code gets scanned.

const [shouldScan, setShouldScan] = useState(true);

Next, create the onScanBarcode function to handle a scanned code. This function will read the code, and show an alert on screen with the code’s value while handling the shouldScan state:

const onScanBarcode = (results) => {
  const scannedCode = results?.code?.[0] ?? "";
  setShouldScan(false); // Stop scanning before showing the alert
  Alert.alert(
    "Scanned Code",
    scannedCode,
    [
      {
        text: "OK",
        onPress: () => setShouldScan(true), // Resume scanning after alert is dismissed
      },
    ],
    { cancelable: false }
  );
};

Below is the complete code for the App.tsx file after adding barcode scanning functionality:

import React, { useState } from "react";
import {
  SafeAreaView,
  StatusBar,
  StyleSheet,
  Text,
  View,
  Alert,
} from "react-native";
import { ScanCode } from "rn-barcode-zxing";

const App = () => {
  const [shouldScan, setShouldScan] = useState(true);

  const onScanBarcode = (results) => {
    const scannedCode = results?.code?.[0] ?? "";
    setShouldScan(false); // Stop scanning before showing the alert
    Alert.alert(
      "Scanned Code",
      scannedCode,
      [
        {
          text: "OK",
          onPress: () => setShouldScan(true), // Resume scanning after alert is dismissed
        },
      ],
      { cancelable: false }
    );
  };

  return (
    <SafeAreaView style={[{ flex: 1 }]}>
      <View style={{ flex: 1 }}>
        <ScanCode shouldScan={shouldScan} onScanBarcode={onScanBarcode} />
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  results: {
    position: "absolute",
    top: 100,
    left: 20,
  },
  code: {
    color: "#FFFFFF",
    fontSize: 20,
    fontWeight: "600",
  },
});

export default App;

Step 6: Test the app

To test your application, run the following command, depending on the target platform:

npx react-native run-android
npx react-native run-ios

In the app, try scanning a barcode. When the code is scanned, an alert will display the code value on the screen.

Our ZXing React Native barcode scanner in action

Disadvantages of using the ZXing barcode scanner library

ZXing provides decent performance for basic barcode scanning tasks but sometimes struggles with more challenging scenarios. Its most notable drawbacks as a barcode scanning solution include:

  • Integration complexity: Integrating ZXing into your app can be cumbersome, especially for developers who may not be familiar with its architecture. This can lead to longer development times and increased chances of bugs during implementation.
  • Scanning accuracy and speed: ZXing struggles with scanning poorly lit or damaged barcodes. It often exhibits low recognition rates for smaller barcodes and can fail to decode them altogether.
  • Compatibility issues: ZXing may not perform reliably across all devices, particularly newer models. This has led to frustration among developers who require consistent performance across different platforms.
  • Lack of active development: As an open-source project, ZXing has seen limited updates and maintenance in recent years. This stagnation can lead to unresolved bugs and compatibility issues, making it less reliable for commercial applications.

For companies that heavily rely on barcode scanning in their business processes, we recommend using an enterprise-grade solution instead.

Building a React Native Barcode Scanner with the Scanbot SDK

We developed the Scanbot Barcode Scanner SDK to help enterprises overcome the hurdles presented by free barcode scanning software. Our goal was to have a developer-friendly solution available for a wide range of platforms that consistently delivers high-quality results – even in challenging circumstances.

In the following section, we’ll show you how easy it is to integrate our Barcode Scanner SDK into your React Native app. Thanks to our SDK’s Ready-to-Use UI Components, you can even use an AR overlay to display multiple barcodes’ contents right in the viewfinder.

React Native Barcode Scanner SDK integration tutorial

We’re going to follow these steps to build our app:

  1. Preparing the project
  2. Installing the SDK
  3. Initializing the SDK
  4. Implementing the scanning modes
  5. Building the app and scanning some barcodes

Requirements

We’re going to use Expo in this tutorial, so you’ll need to set up your local development environment. The steps for preparing your local environment for development can be found in the Expo documentation.

💡 Starting with React Native version 0.75, it’s recommended to use frameworks such as Expo to develop React Native applications. We’ll use Expo in this tutorial as well.

Of course, you can also integrate our SDK without a framework. For further information, please refer to the official React Native documentation.

Step 1: Prepare the project

First, let’s create our Expo app using the Expo CLI, which will walk us through the setup process. To create an Expo app, run the following command in the terminal:

  npx create-expo-app@latest

Let’s open the project with your favorite code editor and prepare it to install the Scanbot SDK.

Remove boilerplate code

The Expo CLI has generated some screens to ensure the app isn’t empty. We can remove the generated code quickly by running the following command:

npm run reset-project

Now the app directory only contains an index file, which is our only screen, and a _layout.tsx file.

⚠️ Npm is the default package manager in this project, so we’ll be using it for this tutorial. Feel free to set up the project with other package managers supported by Expo.

Generate the native projects

To install expo-dev-client, we need to run the following command:

npx expo install expo-dev-client

After that, we generate our iOS and Android projects with:

npx expo prebuild

⚠️ If you are using a React Native Barcode Scanner SDK trial licence, make sure that the Android applicationId and iOS bundle identifier are the same.

Step 2: Install the SDK

Let’s install and configure the React Native Barcode Scanner SDK.

First we need to run:

npx expo install react-native-scanbot-barcode-scanner-sdk

Now that the npm package has been installed, all that’s left is to make the necessary native changes to the projects.

You can use our config plugin or manually configure the native projects. We’ll showcase both so you can pick the method you prefer.

Method A: Expo config plugin

To utilize the plugin, add the following to your app.json file:

"plugins": [
  "expo-router",
  [
    "react-native-scanbot-barcode-scanner-sdk",
    {
      "iOSCameraUsageDescription": "Camera permission is needed to scan barcodes",
      "androidCameraPermission": true,
      "androidCameraFeature": true,
      "mavenURLs": true
    }
  ]
],

Then run:

npx expo prebuild

Now we’re all set. You can skip the Android and iOS native changes when using this plugin.

Method B: Manual configuration

Alternatively, you can also apply the changes to the native projects manually.

Android

For Android, we first need to add the camera permissions in android/app/src/main/AndroidManifest.xml.

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

For development builds, we also need to add our Maven package URLs in android/build.gradle.

allprojects {
    repositories {
        ... other maven rpositories
      maven { url "https://nexus.scanbot.io/nexus/content/repositories/releases/" }
      maven { url "https://nexus.scanbot.io/nexus/content/repositories/snapshots/" }
    }
}

💡 For more information about individual releases, please refer to our changelog.

iOS

For iOS, we need to include a description for the camera permission in ios/{projectName}/Info.plist anywhere inside the element:

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

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

Step 3: Initialize the SDK

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

There are many ways to initialize the SDK that depend on your use case. In our example, we’re going to initialize the SDK inside a useEffect in our _layout.txt file.

ScanbotBarcodeSDK
  .initializeSdk({ licenseKey: "" })
  .then(result => console.log(result))
  .catch(err => console.log(err));

So the _layout.txt would look like this:

import { Stack } from "expo-router";
import { useEffect } from "react";
import ScanbotBarcodeSDK from "react-native-scanbot-barcode-scanner-sdk"

export default function RootLayout() {

useEffect(() => {
  ScanbotBarcodeSDK
    .initializeSdk({ licenseKey: "" })
    .then(result => console.log(result))
    .catch(err => console.log(err));
}, []);

  return (
    <Stack>
      <Stack.Screen name="index" />
    </Stack>
  );
}

💡 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 modes

Our RTU UI components make it easy to deploy our Barcode Scanner SDK’s different scanning modes in your app. Let’s start with the simplest use case: single-barcode scanning.

In app/index.tsx, we’re going to add a button that will start the scanning process. After we ran the reset project script, the index.tsx should only contain a single view with a text component. Let’s replace the Text component with a button component like this:

  <Button title={"Start single barcode scanning"} onPress={onSingleBarcodeScan} />

Now we need to define onBarcodeScannerPress, which will start the single-barcode scanning mode:

const onSingleBarcodeScan = useCallback(async () => {
  try {
    /** Check license status and return early if the license is not valid */
    if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
      return;
    }
    /**
     * Instantiate a configuration object of BarcodeScannerConfiguration and
     * start the barcode scanner with the configuration
     */
    const config = new BarcodeScannerConfiguration();
    /** Initialize the use case for single scanning */
    config.useCase = new SingleScanningMode();
    /** Start the BarcodeScanner */
    const result = await startBarcodeScanner(config);
    /** Handle the result if result status is OK */
    if (result.status === 'OK' && result.data) {
      Alert.alert(
        "Barcode Scanning successfully!",
        `${result.data.items.map(barcode =>
          `Barcode value: ${barcode.text} and type: ${barcode.type}`
        ).join("\n")}`);
    } else {
      console.log("The user has canceled the Barcode Scanning")
    }
  } catch (e: any) {
    console.log("An error has occurred while running Barcode Scanner", e.message);
  }
}, []);

Our final screen should look like this:

import {Alert, Button, View} from "react-native";
import {useCallback} from "react";
import {
  BarcodeScannerConfiguration,
  SingleScanningMode,
  startBarcodeScanner
} from "react-native-scanbot-barcode-scanner-sdk/ui_v2";
import ScanbotBarcodeSDK from "react-native-scanbot-barcode-scanner-sdk";

export default function Index() {

  const onSingleBarcodeScan = useCallback(async () => {
    try {
      /** Check license status and return early if the license is not valid */
      if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
        return;
      }
      /**
       * Instantiate a configuration object of BarcodeScannerConfiguration and
       * start the barcode scanner with the configuration
       */
      const config = new BarcodeScannerConfiguration();
      /** Initialize the use case for single scanning */
      config.useCase = new SingleScanningMode();
      /** Start the BarcodeScanner */
      const result = await startBarcodeScanner(config);
      /** Handle the result if result status is OK */
      if (result.status === 'OK' && result.data) {
        Alert.alert(
          "Barcode Scanning successfully!",
          `${result.data.items.map(barcode =>
            `Barcode value: ${barcode.text} and type: ${barcode.type}`
          ).join("\n")}`);
      } else {
        console.log("The user has canceled the Barcode Scanning")
      }
    } catch (e: any) {
      console.log("An error has occurred while running Barcode Scanner", e.message);
    }
  }, []);

  return (
    <View
      style={{
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <Button title={"Start single barcode scanning"} onPress={onSingleBarcodeScan}/>
    </View>
  );
}

Now we’re ready to start building our app and scan some barcodes. Let’s also add the multi-barcode scanning and AR overlay modes in the same way:

const onMultiBarcodeScan = useCallback(async () => {
  try {
    /** Check license status and return early if the license is not valid */
    if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
      return;
    }
    /**
     * Instantiate a configuration object of BarcodeScannerConfiguration and
     * start the barcode scanner with the configuration
     */
    const config = new BarcodeScannerConfiguration();
    /** Initialize the use case for multi-scanning */
    config.useCase = new MultipleScanningMode();
    /** Start the BarcodeScanner */
    const result = await startBarcodeScanner(config);
    /** Handle the result if result status is OK */
    if (result.status === 'OK' && result.data) {
      Alert.alert(
        "Barcode Scanning successfully!",
        `${result.data.items.map(barcode =>
          `Barcode value: ${barcode.text} and type: ${barcode.type}`
        ).join("\n")}`);
    } else {
      console.log("The user has canceled the Barcode Scanning")
    }
  } catch (e: any) {
    console.log("An error has occurred while running Barcode Scanner", e.message);
  }
}, []);

const onAROverlayBarcodeScan = useCallback(async () => {
  try {
    /** Check license status and return early if the license is not valid */
    if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
      return;
    }
    /**
     * Instantiate a configuration object of BarcodeScannerConfiguration and
     * start the barcode scanner with the configuration
     */
    const config = new BarcodeScannerConfiguration();
    /** Initialize the use case for multi-scanning */
    config.useCase = new MultipleScanningMode();
    /** Configure AR Overlay. */
    config.useCase.arOverlay.visible = true;
    config.useCase.arOverlay.automaticSelectionEnabled = false;
    /** Start the BarcodeScanner */
    const result = await startBarcodeScanner(config);
    /** Handle the result if result status is OK */
    if (result.status === 'OK' && result.data) {
      Alert.alert(
        "Barcode Scanning successfully!",
        `${result.data.items.map(barcode =>
          `Barcode value: ${barcode.text} and type: ${barcode.type}`
        ).join("\n")}`);
    } else {
      console.log("The user has canceled the Barcode Scanning")
    }
  } catch (e: any) {
    console.log("An error has occurred while running Barcode Scanner", e.message);
  }
}, []);

Finally, we need to add the buttons to start the scanning modes:

  <Button title={"Start multi-barcode scanning"} onPress={onMultiBarcodeScan}/>
  <Button title={"Start AR Overlay barcode scanning"} onPress={onAROverlayBarcodeScan}/>

Step 5: Build the app and scanning some barcodes

We’re all done setting up our app.

To run the app on your Android or iOS device, use the following commands:

For Android:

npx expo run:android --device

For iOS:

npx expo run:ios --device

Now you can go ahead and scan 1D and 2D barcodes – one at a time, several at once, or with a real-time preview of their values via the AR overlay!

And if you’re in need of some sample barcodes for testing purposes, we’ve got you covered:

Various barcodes for testing

Conclusion

If this tutorial has piqued your interest in integrating barcode scanning functionalities into your React Native app, make sure to take a look at our SDK’s other neat features in our documentation or run our example project.

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

Happy coding!