> For the complete documentation index, see [llms.txt](https://docs.accurascan.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.accurascan.com/language/web-plugin/id-plugin/react.md).

# React

## Accura IDScan Plugin — React Integration Guide

This guide walks you through integrating the **Accura IDScan Plugin** into a React project built with Vite.

### Step 1: Initialize Project

If you do not have an existing React project, scaffold one using Vite:

```bash
npm create vite@latest my-id-app -- --template react
cd my-id-app
npm install
```

***

### Step 2: Install Plugin

Install the Accura IDScan Plugin package from the npm registry:

```bash
npm install accuraidscanplugin
```

***

### Step 3: TypeScript Support *(Recommended)*

If your project uses TypeScript, create a type declaration file at `src/types.d.ts` to suppress module resolution warnings:

```typescript
declare module 'accuraidscanplugin';
```

***

### Step 4: Implementation

Create a dedicated component file `src/IDScanner.jsx` (or `.tsx`). The following snippet shows only the **plugin import and instantiation** logic:

```jsx
import { useEffect, useRef } from 'react';

const IDScanner = () => {
    const pluginRef = useRef(null);       // Holds the active plugin instance across renders
    const initialized = useRef(false);   // Guards against double-initialization in Strict Mode

    useEffect(() => {
        // Prevent re-initialization caused by React Strict Mode's double-invocation behavior
        if (initialized.current) return;
        initialized.current = true;

        // Dynamically import the plugin to ensure it runs only in the browser context.
        // This prevents errors in SSR environments and improves initial bundle performance.
        import("accuraidscanplugin").then((Module) => {
            const IDCardPlugin = Module.default;

            // Instantiate the IDCardPlugin with:
            //   1. The capture callback invoked on successful ID scan completion
            //   2. A configuration object specifying the target card and UI appearance
            pluginRef.current = new IDCardPlugin(
                handleCapture,   // Fired automatically when the scan is complete
                {
                    countryCode: "UGA",    // country code of the card
                    cardCode: "UGNIDF",    // Card code for the front-side ID
                    topTextSize: "",       // Top overlay text size (default if empty)
                    topTextColor: "",      // Top overlay text color (default if empty)
                    topTextWeight: "",     // Top overlay font weight (default if empty)
                    bottomTextSize: "",    // Bottom overlay text size (default if empty)
                    bottomTextColor: "",   // Bottom overlay text color (default if empty)
                    bottomTextWeight: "",  // Bottom overlay font weight (default if empty)
                }
            );

            // Initialize the camera and begin the ID card detection session.
            pluginRef.current.start().then(() => {
                console.log("ID Scanner Engine Ready");
            });
        });

        // Cleanup: destroy the plugin instance when the component unmounts
        return () => {
            if (pluginRef.current) {
                pluginRef.current.destroy();
                pluginRef.current = null;
            }
        };
    }, []);

    return <></>;
};
```

***

### Step 5: Response Handling

When the plugin completes an ID card scan, it invokes the **`handleCapture`** callback with a payload object that may contain one or both of the following properties:

| Property | Type     | Description                                                     |
| -------- | -------- | --------------------------------------------------------------- |
| `front`  | `string` | Base64 Data URL of the front side of the ID card                |
| `back`   | `string` | Base64 Data URL of the back side of the ID card (if applicable) |

**What is Base64?** Base64 is a binary-to-text encoding scheme that converts raw binary image data into a sequence of printable ASCII characters. Each scanned card image is delivered as a Data URL string (e.g., `data:image/jpeg;base64,/9j/...`), combining a MIME type prefix with the encoded image payload. Before transmitting to a server, this string must be decoded back into binary form (a `Blob`) to construct a valid multipart HTTP request.

The following demonstrates the **base64-to-Blob conversion** and API submission:

```jsx
// Utility: converts a base64 Data URL string into a binary Blob.
// MultiPart form uploads require raw binary data rather than base64-encoded text.
const base64ToBlob = (base64DataURL) => {
    // Split at the comma: left = MIME header, right = encoded payload
    const [meta, content] = base64DataURL.split(",");

    // Extract the MIME type (e.g., "image/jpeg") from the header
    const mimeMatch = meta.match(/:(.*?);/);
    const mime = mimeMatch ? mimeMatch[1] : "image/jpeg";

    // Decode the base64 payload back into raw binary characters
    const binary = atob(content);

    // Re-construct binary data as a typed array of unsigned 8-bit integers
    const array = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
        array[i] = binary.charCodeAt(i); // Convert each character to its byte value
    }

    // Wrap the binary array in a Blob with the correct MIME type
    return new Blob([array], { type: mime });
};

// Sends a card image Blob to the server-side verification endpoint.
const sendToAPI = async (blob, isface, card_code, filename) => {
    const formData = new FormData();
    formData.append("scan_image", blob, filename);  // The binary image file
    formData.append("isface", isface);              // "front" or "back"
    formData.append("country_code", "UGA");         // ISO country code
    formData.append("card_code", card_code);        // Card template identifier
    formData.append("passport", "false");           // "true" for passport documents
    formData.append("webcam", "false");             // "true" if captured via webcam

    try {
        const response = await fetch("http://ip:port/doc_liveness.php", {
            method: "POST",
            body: formData,
        });
        const data = await response.json();
        console.log(`API Response (${isface}):`, data);

        if (data && data.score !== undefined) {
            console.log(`Score (${isface}): ${data.score}`);
        }
    } catch (error) {
        console.error(`Error sending ${isface} to API:`, error);
    }
};

// Primary capture callback — invoked by the plugin when scanning is complete.
const handleCapture = async (base64) => {
    if (base64.front) {
        const frontBlob = base64ToBlob(base64.front);
        await sendToAPI(frontBlob, "front", "UGNIDF", "front.jpg");
    }
    if (base64.back) {
        const backBlob = base64ToBlob(base64.back);
        await sendToAPI(backBlob, "back", "UGNIDB", "back.jpg");
    }
};
```

***

### Step 6: Demo Implementation

The following is the **complete, production-ready component**. Copy and paste it directly into `src/IDScanner.jsx`. The original logic is preserved exactly as-is.

```jsx
import React, { useEffect, useRef, useState } from 'react';

const IDScanner = () => {
    const pluginRef = useRef<any>(null);
    const [ready, setReady] = useState(false);
    const initialized = useRef(false);

    const base64ToBlob = (base64DataURL: string) => {
        const [meta, content] = base64DataURL.split(",");
        const mimeMatch = meta.match(/:(.*?);/);
        const mime = mimeMatch ? mimeMatch[1] : "image/jpeg";
        const binary = atob(content);
        const array = new Uint8Array(binary.length);
        for (let i = 0; i < binary.length; i++) {
            array[i] = binary.charCodeAt(i);
        }
        return new Blob([array], { type: mime });
    };

    const sendToAPI = async (blob: Blob, isface: string, card_code: string, filename: string) => {
        const formData = new FormData();
        formData.append("scan_image", blob, filename);
        formData.append("isface", isface);
        formData.append("country_code", "UGA");
        formData.append("card_code", card_code);
        formData.append("passport", "false");
        formData.append("webcam", "false");

        try {
            console.log(`Sending ${isface} (${card_code}) to API...`);
            const response = await fetch("http://ip:port/doc_liveness.php", {
                method: "POST",
                body: formData,
            });

            const data = await response.json();
            console.log(`API Response (${isface}):`, data);

            if (data && data.score !== undefined) {
                console.log(`Score (${isface}): ${data.score}`);
            }
        } catch (error) {
            console.error(`Error sending ${isface} to API:`, error);
        }
    };


    const handleCapture = async (base64: any) => {
        console.log("Capture result received:", Object.keys(base64));

        if (base64.front) {
            console.log("Processing front side...");
            const frontBlob = base64ToBlob(base64.front);
            await sendToAPI(frontBlob, "front", "UGNIDF", "front.jpg");
        }
        if (base64.back) {
            console.log("Processing back side...");
            const backBlob = base64ToBlob(base64.back);
            await sendToAPI(backBlob, "back", "UGNIDB", "back.jpg");
        }
    };

    useEffect(() => {
        if (initialized.current) return;
        initialized.current = true;

        console.log("Initializing ID Scanner plugin...");

        import("accuraidscanplugin").then((Module) => {
            const IDCardPlugin = Module.default;
            console.log("Plugin Module loaded");

            pluginRef.current = new IDCardPlugin(handleCapture, {
                countryCode: "UGA",
                cardCode: "UGNIDF",
                topTextSize: "",
                topTextColor: "",
                topTextWeight: "",
                bottomTextSize: "",
                bottomTextColor: "",
                bottomTextWeight: "",
            } as any);

            console.log("Starting plugin engine...");
            pluginRef.current.start().then(() => {
                console.log("ID Scanner Engine Ready and Camera should be open");
                setReady(true);
            }).catch((err: any) => {
                console.error("Engine failed to start:", err);
            });
        }).catch(err => console.error("Plugin dynamic import failed:", err));

        return () => {
            if (pluginRef.current) {
                console.log("Destroying ID Scanner plugin...");
                pluginRef.current.destroy();
                pluginRef.current = null;
            }
        };
    }, []);


    return (
        <></>
    );
};

export default IDScanner;
```

***

### Step 7: Usage

Import and render the component in `App.jsx`:

```jsx
import IDScanner from './IDScanner';

export default function App() {
  return <IDScanner />;
}
```

> **Note:** Remove the `<StrictMode>` wrapper from `main.jsx` or `main.tsx` if present, as React Strict Mode invokes lifecycle hooks twice in development, which can cause duplicate plugin initialization.

***

### Step 8: Running the Project

```bash
npm run dev
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.accurascan.com/language/web-plugin/id-plugin/react.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
