React Advanced SDK

📘

The React Advanced SDK is in early release.

This is the documentation for the Berbix React Advanced SDK which lets you compose a Berbix ID verification flow using your own UI components (as opposed to our React SDK, which is a one-line solution to integrate Berbix ID verification).

🚧

Does your integration require custom UI?

If not, and you just need a drop-in ID verification flow, you may be better served by our standard React SDK. The React Advanced SDK enables use cases, such as custom UI or deeper integration into your app, that cannot be covered by the React SDK.

Getting started

Your app needs to be wrapped in a CameraProvider to provide the appropriate context to the SDK's other components.

import { CameraProvider } from "@berbix/react-advanced-sdk";

export default function App({ children }) {
  return (
    <html>
      <head>
        <meta />
      </head>
      <body>
        <CameraProvider>{children}</CameraProvider>
      </body>
    </html>
  );
}

Learn more about CameraProvider in its API docs.

Showing the stream from the camera

To render the output from the device's camera, use the CameraStream component. This component is essentially a managed <video> element; it takes care of permissions and selection of device cameras, as well as setting up and running real-time shape detection on the camera stream.

It also provides various callbacks that developers can use to detect or respond to various events. You can learn more about this in the CameraStream API docs.

import { CameraStream } from "@berbix/react-advanced-sdk";

function MyPage() {
  return (
    <div>
      <CameraStream
        fit="cover"
        onStreamStart={() => console.log("Stream started")}
        onStreamEnd={() => console.log("Stream ended")}
      />
    </div>
  );
}

Laying out the camera stream

The pixel dimensions of the rendered <CameraStream> element depend on the resolution of the device's camera. Often, this is much larger than the viewport size, leading to scroll bars on the page if not sized correctly. As such, we recommend developers explicitly set the size of the element:

// Size the element to fully cover its parent container
style = { width: "100%", height: "100%" };

// Size the element to fully cover the viewport
style = { width: "100vw", height: "100vw" };

// Set a fixed size on the element
style = { width: "100px", height: "100px" };

<CameraStream style={style} />;

The CameraStream component uses the object-fit CSS property (aliased in the component as the fit prop) to determine how to crop or scale the video stream to fit the dimensions of the element. By default, fit is set to cover, meaning that the video stream will be cropped to ensure that it fully covers the element. Below is an overview of how each fit value behaves:

fit valueNo croppingNo video barsPreserve aspect ratio
cover✅✅
contain✅✅
fill✅✅

The MDN docs on object-fit has demos of these different values. You can read the docs here.

Adding shape detection

The CameraStream component has the built-in ability to do real-time shape detection. You can do this as follows:

<CameraStream
  shapeDetector="barcode"
  shapeDetectorProps={{
    active: true,
    onDetect: () => console.log("Shape detected"),
  }}
/>

You can read more about the various options for the shape detector in the shapeDetectorProps section of the docs.

Controlling the camera output

We provide a useCamera hook that gives you functions to control the CameraStream component. You can read about the hook and its functions here.

Capturing camera frames

There are two ways to capture camera frames: either through the capture() function in the useCamera hook, or using shapeDetectorProps.autoCapture. The former needs to be imperatively called; the latter will automatically trigger a capture if a shape is successfully detected.

In either case, it will fire the onCapture callback in the CameraStream component, passing in a Blob of the captured image from the stream. You can add a handler to handle this:

const [active, setActive] = useState(true);

<CameraStream
  onCapture={(blob, shapes) => {
    setActive(false);

    // Do some handling of the blob, e.g. send it to an API.
  }}
  shapeDetector="barcode"
  shapeDetectorProps={{
    active,
    autoCapture: true,
  }}
/>;

Components

CameraProvider

This component performs the setup necessary for the camera stream to function; internally, it uses React context. It needs to be the parent of any CameraStream elements and useCamera calls, and there should only ever be a single CameraProvider on a page.

We recommend that you put CameraProvider as close to the root component as possible.

Examples

import { CameraProvider } from "@berbix/react-advanced-sdk";

function MyAppShell({ children }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="initial-scale=1, width=device-width" />
      </Head>
      <CameraProvider>{children}</CameraProvider>
    </>
  );
}

CameraStream

This component renders a live stream from the device's camera. It is internally implemented as a <video> element, and the CameraStream component will pass any standard React HTML props (like children, style, className, width, height etc.) to that element. As such, you have complete control over how it is laid out and styled in the DOM, as well as direct access to the underlying <video> element via ref, if necessary.

The size of the <video> element in the page layout varies depending on the quality of the user's camera, but it may be up to 2048px on either side. As such, any styling or layout should accommodate that possibility (e.g. by setting absolute widths on the component via width or style).

Note that the stream will start as soon as the component and camera are initialized.

Props

NameTypeDefaultDescription
fit"cover" \| "contain" \| "fill" \| "none""cover"Specify how to fit the camera stream into the <video> element. See the MDN docs on CSS object-fit (which this prop aliases) to see how these values differ in behavior.
direction"user" \| "environment""environment"Specify the direction of the camera to use. "user" is the front-facing camera on mobile devices, appropriate for capturing selfies. "environment" is the back-facing camera, appropriate for capturing IDs.
onUnsupportedEnvironment(message: string) => voidCallback fired when the browser or browsing environment does not support camera streaming or capture. This is typical in legacy browsers or some embedded web views in native mobile applications (e.g. WeChat, Instagram, etc.). The callback function is passed a human-readable error message.

This is a terminal callback, so the component will no longer fire any callbacks after this.
onCameraUnavailable(message: string) => voidCallback fired when no camera is available on the device, or if the available camera does not have the quality required for ID verification. The callback function is passed a human-readable error message.

This is a terminal callback, so the component will no longer fire any callbacks after this.
onStreamDeny(message: string) => voidCallback fired when the user denies access to the device's camera resource, or if the system automatically denies access to the resource.

This is a terminal callback, so the component will no longer fire any callbacks after this.
onStreamFail(message: string) => voidCallback fired when a hardware issue prevents the camera or camera stream from being accessed.

This is a terminal callback, so the component will no longer fire any callbacks after this.
onStreamStart(void) => voidCallback fired when the stream from the camera is started. This is typically called only once, when the component is mounted. For example, calling the resume() function in the hook will not fire this callback.
onStreamStop(void) => voidCallback fired when the stream from the camera is stopped. This is typically called only once, when the component is unmounted. For example, calling the pause() function in the hook will not fire this callback.
onCapture(blob: Blob, shapes?: ShapePayload[]) => voidCallback fired when a frame is successfully captured from the camera stream. This is caused either by a user action (e.g. a button press that calls the capture() method) or an auto-capture from the shape detector. The callback function is passed a Blob, which you can use to render into an <img> or post-process as necessary.

If the capture was initiated automatically by the shape detector, it will also be passed a ShapePayload, which contains data about the detected shape.
shapeDetector"barcode" \| "selfie" \| "id"Set the type of shape detector to use on the camera stream. If left undefined, no shape detection will be run.
shapeDetectorPropsDefined below{}

shapeDetectorProps props

NameTypeDefaultDescription
activebooleanfalseIf true, the shape detector will run. Note that while active is true, the onDetect callback will continue to be fired.
autoCapturebooleanfalseIf true and active is also set to true, the shape detector will automatically fire the onCapture callback in the CameraStream component when a shape is detected.
onDetect(shapes?: ShapePayload[]) => voidCallback fired while the shape detector is active. This will continuously fire at roughly 500ms intervals. If a shape is detected, the callback will be passed an array of shape payloads that contain information about the detected shape(s). If no shape was detected, this value will be undefined.

The DOMRect that is in ShapePayload has been transformed to overlay directly on the <video> element.

ShapePayload object

When shapeDetector is set to "barcode", the payload is an object with the following properties:

NameTypeDefaultDescription
boundingBoxDOMRectThe bounds of the detected barcode.
format"pdf417"The format of the barcode. At the moment, the SDK only supports the detection of PDF417 barcodes.
rawValuestringThe string that is encoded in the barcode.

When shapeDetector is set to "id", the payload is an object with the following properties:

NameTypeDefaultDescription
boundingBoxDOMRectThe bounds of the detected ID.
type"card"The type of the ID. The SDK may support different ID types in the future, but at the moment, it's statically set to "card".

When shapeDetector is set to "selfie", the payload is an object with the following properties:

NameTypeDefaultDescription
boundingBoxDOMRectThe bounds of the detected face.
type"left" \| "right" \| "direct"The direction that the selfie (read: face) is facing.

Examples

Basic example

The following example implements a basic stream from the camera, with a simple console output when a
camera capture is invoked.

import { CameraStream } from "@berbix/react-advanced-sdk";

function CameraStreamScreen() {
  return <CameraStream onCapture={(blob, shapes) => console.log("Captured!", blob, shapes)} />;
}

Advanced example

The next example implements a slightly more advanced live camera screen with shape detection.
It uses the payload from the onDetect callback to appropriately position and size
a barcode overlay.

import { CameraStream } from "@berbix/react-advanced-sdk";

function BarcodeCaptureScreen() {
  const [barcodeBbox, setBarcodeBbox] = useState<BoundingBox | null>(null);
  const [detectorActive, setDetectorActive] = useState(true);

  return (
    <>
      <CameraStream
        onCapture={(blob, barcode) => {
          setDetectorActive(false);
          // Call out to an API
        }}
        shapeDetector={ShapeDetector.Barcode}
        shapeDetectorProps={{
          active: detectorActive,
          autoCapture: true,
          onDetect: (shapes) => {
            if (shapes.length) setBarcodeBbox(shapes[0].boundingBox);
          },
        }}
      />
      {/* Highlight an identified barcode using a red rect outline */}
      <div
        style={{
          position: "absolute",
          top: barcodeBbox.top,
          left: barcodeBbox.left,
          width: barcodeBbox.width,
          height: barcodeBbox.height,
          border: "1px solid red",
        }}
      >
        &nbsp;
      </div>
    </>
  );
}

useCamera hook

This React hook provides functions to fire callbacks on the camera components.

Props

NameTypeDescription
capture() => voidTake a photo using the active camera. This will fire the onCapture callback in the CameraStream component.
pause() => voidPause the camera stream from a playing state. If the stream is already paused, this will do nothing.
resume() => voidResume the camera stream from a paused state. If the stream is already playing, this will do nothing.

Examples

const { capture, pause, stopShapeDetector } = useCamera();

function MyComponent() {
  return (
    <>
      <button onClick={() => pause()}>Pause camera</button>
      <button onClick={() => capture()}>Take a picture</button>
    </>
  );
}

References