Tutorial: Building a QR Scanner with React Native using RNCamera

This tutorial explains how to create a React Native app capable of real-time QR code scanning and content display upon detection using React Native’s CLI Quickstart.

(Note: If you encounter any setup issues, refer to the React Native Getting Started page and select the “React Native CLI Quickstart” option, as the “Expo CLI Quickstart” is the default at the time of writing.)

Why Choose React Native for a Scanner?

React Native is a powerful framework leveraging React’s principles to enable rapid, cross-platform development of responsive user interfaces. Facebook, Airbnb, Uber, and many others have already embraced it for their latest apps.

Understanding React Native Camera

React Native Camera (RNCamera) is the leading component for integrating camera functionality in React Native apps. It simplifies interaction with native OS features through straightforward functions, eliminating the complexity of native code. RNCamera offers support for:

  • Taking photos
  • Recording videos
  • Face detection
  • Barcode scanning
  • Text recognition (Android only)

Importantly, use the updated RNCamera component instead of the deprecated RCTCamera to ensure access to the latest updates.

Note: React Native Camera heavily relies on the Expo camera module, and going back and forth between the two is quite straightforward.

Building Your First App with RNCamera

Before diving into the React Native QR scanner, let’s install the necessary dependencies.

Installing RNCamera’s Dependencies

The setup requires at least JDK version 1.7 (likely already installed) and, for Android, buildToolsVersion above 25.0.2. (Refer to a more detailed and up-to-date list in the docs for confirmation.)

Begin by creating a new React Native project:

1
react-native init CameraExample

Deploy the initial version of our React Native QR scanner example on your phone:

1
2
cd CameraExample
react-native run-android

Your screen should resemble this:

The Android Emulator showing a React Native welcome screen.

Now, install the react-native-camera package using the recommended “Mostly automatic install with react-native” option, as it proves to be the most efficient among others like Mostly automatic install with CocoaPods and Manual install:

1
2
npm install react-native-camera --save
react-native link react-native-camera

Include the following permissions in android/app/src/main/AndroidManifest.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
   package="com.cameraexample">

     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

     <application
       android:name=".MainApplication"

Set the dimension strategy in android/app/build.gradle to avoid errors, as it lacks a default and you’ll get an error if you don’t define it:

1
2
3
4
5
6
7
     defaultConfig {
         applicationId "com.cameraexample"
         minSdkVersion rootProject.ext.minSdkVersion
+        missingDimensionStrategy 'react-native-camera', 'general'
         targetSdkVersion rootProject.ext.targetSdkVersion
         versionCode 1
         versionName "1.0"

Note: The dimension strategy is typically set to general, as shown above. You can opt for mlkit if you wish to use MLKit for text/face/barcode recognition.

After installation, use run-android to integrate the new dependencies:

1
react-native run-android

A successful installation will display the familiar React Native welcome screen on your device or simulator.

Setting Up the Camera

Start by modifying App.js to import RNCamera:

1
import { RNCamera } from 'react-native-camera';

Next, adjust the render function to incorporate the imported RNCamera. Note the style attribute applied to the camera for full-screen display, ensuring visibility:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
   render() {
     return (
       <View style={styles.container}>
-        <Text style={styles.welcome}>Welcome to React Native!</Text>
-        <Text style={styles.instructions}>To get started, edit App.js</Text>
-        <Text style={styles.instructions}>{instructions}</Text>
+        <RNCamera
+          ref={ref => {
+            this.camera = ref;
+          }}
+          style={{
+            flex: 1,
+            width: '100%',
+          }}
+        >
+        </RNCamera>
       </View>
     );
   }

After this step, your interface should feature a full-screen camera, as depicted below:

A screenshot showing a full-screen camera component. The camera is pointed at a computer monitor displaying two samples on our QR code scanner.

Although our React Native barcode scanner can now detect barcodes (as seen with our test QR codes displayed on the monitor), it cannot yet interpret their content. Let’s employ RNCamera’s algorithm to decipher the information embedded within them.

Extracting Barcode Data

To read barcode information, we’ll utilize the onGoogleVisionBarcodesDetected prop. This prop allows us to call a function and extract the necessary data. We’ll add this to the <RNCamera> component and link it to a barcodeRecognized function for processing. Note that onGoogleVisionBarcodesDetected returns an object containing the barcodes property, which holds an array of all detected barcodes within the camera’s view.

Note: The onGoogleVisionBarcodesDetected QR code technology is specific to Android. For a cross-platform approach, consider onBarCodeRead, which supports one barcode at a time. Using it as a fallback is left as an exercise for the reader.

Here’s the updated <RNCamera> component with onGoogleVisionBarcodesDetected:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
       <RNCamera
          ref={ref => {
            this.camera = ref;
          }}
          style={{
            flex: 1,
            width: '100%',
          }}
          onGoogleVisionBarcodesDetected={this.barcodeRecognized}
        >
        </RNCamera>

Now, we can manage the barcodes within App.js using the function below, which will provide a notification when a barcode is detected and should present its content on the screen:

1
2
3
  barcodeRecognized = ({ barcodes }) => {
    barcodes.forEach(barcode => console.warn(barcode.data))
  };

Here’s how it appears in action:

The same camera angle as before, now with console popups at the bottom having decoded the messages in the QR codes: "Hi from QR1" and Toptal's web site address.

Implementing Barcode Overlays

While the previous screenshot displays the barcode’s content, it lacks clarity regarding which message corresponds to each barcode. To address this, we’ll delve into the barcodes data returned by onGoogleVisionBarcodesDetected to precisely locate each barcode on the screen.

First, we’ll store the recognized barcodes in the state for access and overlay rendering based on their data. Here’s the modified function:

1
barcodeRecognized = ({ barcodes }) => this.setState({ barcodes });

We need to introduce a state object initialized with an empty array of barcodes to prevent errors in our render functions:

1
2
3
4
5
export default class App extends Component<Props> {
  state = {
    barcodes: [],
  }
// ...

Now, let’s create the renderBarCodes function, which we’ll place inside the <RNCamera> component:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
        <RNCamera
          ref={ref => {
            this.camera = ref;
          }}
          style={{
            flex: 1,
            width: '100%',
          }}
          onGoogleVisionBarcodesDetected={this.barcodeRecognized}
        >
          {this.renderBarcodes()}
        </RNCamera>

This function will take the recognized barcodes from the state and display them on-screen:

1
2
3
4
5
  renderBarcodes = () => (
    <View>
      {this.state.barcodes.map(this.renderBarcode)}
    </View>
  );

Note that the mapping points to renderBarcode, responsible for rendering each barcode. Some basic styling has been applied for easy identification:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 renderBarcode = ({ bounds, data }) => (
    <React.Fragment key={data + bounds.origin.x}>
      <View
        style={{
          borderWidth: 2,
          borderRadius: 10,
          position: 'absolute',
          borderColor: '#F00',
          justifyContent: 'center',
          backgroundColor: 'rgba(255, 255, 255, 0.9)',
          padding: 10,
          ...bounds.size,
          left: bounds.origin.x,
          top: bounds.origin.y,
        }}
      >
        <Text style={{
          color: '#F00',
          flex: 1,
          position: 'absolute',
          textAlign: 'center',
          backgroundColor: 'transparent',
        }}>{data}</Text>
      </View>
    </React.Fragment>
  );

Each recognized barcode possesses:

  • A bounds property indicating its on-screen location, used for overlay positioning
  • A data property revealing the encoded information
  • A type property specifying the barcode format (2D, QR, etc.)

Using these three parameters in the render function yields the following outcome:

The same camera angle again, this time with the decoded messages overlaid atop their corresponding QR codes.

Cross-platform Deployment Considerations

RNCamera abstracts native camera module components into a universal, cross-platform language. However, certain features might be platform-specific or require different implementations. Before you begin, consult the documentation to confirm platform compatibility for your intended features. Additionally, thorough testing on various devices after implementation is crucial to guarantee product robustness, particularly for edge cases.

Conclusion

This tutorial provided a glimpse into augmented reality development by guiding you through building a basic QR code scanner in React Native, now functional on your device or simulator. Feel free to leave a comment for any questions or requests!

Licensed under CC BY-NC-SA 4.0