> 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/nextjs.md).

# Nextjs

## Accura IDScan Plugin — Next.js Integration Guide

This guide walks you through integrating the **Accura IDScan Plugin** into a Next.js project using the App Router.

### Step 1: Initialize Project

If you do not have an existing Next.js project, create one using the official CLI:

```bash
npx create-next-app@latest my-id-app
cd my-id-app
```

***

### Step 2: Install Plugin

Install the Accura IDScan Plugin package from the npm registry:

```bash
npm install accuraidscanplugin
```

***

### Step 3: TypeScript Support

Since the `accuraidscanplugin` package does not ship with TypeScript declarations, create a type definition file at the project root to resolve module errors:

```typescript
// types.d.ts (place in the project root)
declare module 'accuraidscanplugin';
```

Ensure this file is referenced in your `tsconfig.json`'s `include` array:

```json
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types.d.ts"]
```

***

### Step 4: Implementation

Create `components/IDScanner.tsx`. The snippet below demonstrates only the **plugin import and instantiation** — the minimal code required to activate the ID scan engine in a Next.js client component:

```tsx
"use client"; // Required: marks this as a Client Component for browser-only execution

import { useEffect, useRef } from "react";

export default function IDScanner() {
    const pluginRef = useRef(null);        // Persists the plugin instance across renders
    const initialized = useRef(false);    // Prevents double-init from React Strict Mode

    useEffect(() => {
        // Exit early if the plugin has already been initialized
        if (initialized.current) return;
        initialized.current = true;

        // Dynamically import the plugin at runtime to avoid SSR-related errors.
        // Next.js renders components on the server by default; browser-dependent APIs
        // (camera, DOM manipulation) are unavailable in that environment.
        // Dynamic import defers execution to the client side exclusively.
        import("accuraidscanplugin").then((Module) => {
            const IDCardPlugin = Module.default;

            // Instantiate the plugin with:
            //   1. The capture callback: invoked when scan is complete
            //   2. Configuration: specifies the target country, card template, and UI styling
            pluginRef.current = new IDCardPlugin(
                handleCapture,   // Called automatically upon successful ID card scan
                {
                    countryCode: "UGA",    // country code of the card
                    cardCode: "UGNIDF",    // Card code of the card
                    topTextSize: "",       // Top instruction overlay text size (default if empty)
                    topTextColor: "",      // Top instruction overlay text color (default if empty)
                    topTextWeight: "",     // Top instruction overlay font weight (default if empty)
                    bottomTextSize: "",    // Bottom instruction overlay text size (default if empty)
                    bottomTextColor: "",   // Bottom instruction overlay text color (default if empty)
                    bottomTextWeight: "",  // Bottom instruction overlay font weight (default if empty)
                }
            );

            // Launch the camera interface and begin the ID card detection session.
            pluginRef.current.start().then(() => {
                console.log("ID Scanner Ready");
            });
        }).catch(err => console.error("Plugin failed to load:", err));

        // Cleanup: invoked when the component unmounts (e.g., page navigation).
        // Ensures the camera stream is released and all plugin resources are freed.
        return () => {
            if (pluginRef.current) {
                pluginRef.current.destroy();
            }
        };
    }, []);

    return <></>;
}
```

***

### Step 5: Response Handling

When the plugin completes an ID card scan, it invokes the **`handleCapture`** callback with a payload object. This object 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:

```tsx
// Utility: converts a base64 Data URL string into a binary Blob.
// Multipart form uploads require raw binary data rather than text-encoded base64.
const base64ToBlob = (base64DataURL: string) => {
    // Separate the MIME type header from the encoded payload at the comma boundary.
    // meta    = "data:image/jpeg;base64"
    // content = "/9j/4AAQSkZJRgAB..."
    const [meta, content] = base64DataURL.split(",");

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

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

    // Allocate a typed byte array of the same length as the decoded binary content.
    const array = new Uint8Array(binary.length);

    // Reconstruct the original binary bytes by mapping each character to its code point.
    for (let i = 0; i < binary.length; i++) {
        array[i] = binary.charCodeAt(i);
    }

    // Wrap the binary array in a Blob with the correct content type for server handling.
    return new Blob([array], { type: mime });
};

// Sends a card image Blob to the server-side verification endpoint via multipart POST.
const sendToAPI = async (blob: Blob, isface: string, card_code: string, filename: string) => {
    const formData = new FormData();
    formData.append("scan_image", blob, filename); // The binary image file
    formData.append("isface", isface);             // "front" or "back" — card side identifier
    formData.append("country_code", "UGA");        // ISO country code of the scanned ID
    formData.append("card_code", card_code);       // Card template identifier
    formData.append("passport", "false");          // Set "true" if the document is a passport
    formData.append("webcam", "false");            // Set "true" if the image was 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);

        // Inspect the document authenticity/liveness score from the response.
        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 automatically when the plugin completes a scan.
// Receives: base64 — payload object with front and/or back card image Data URLs.
const handleCapture = async (base64: any) => {
    console.log("Capture result:", base64);

    // Convert and dispatch the front card image if present in the payload.
    if (base64.front) {
        const frontBlob = base64ToBlob(base64.front);
        await sendToAPI(frontBlob, "front", "UGNIDF", "front.jpg");
    }

    // Convert and dispatch the back card image if present in the payload.
    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 `components/IDScanner.tsx`. The original logic is preserved exactly as-is.

```tsx
"use client";

import { useEffect, useRef, useState } from "react";

export default function IDScanner() {
    const pluginRef = useRef(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); //Upload your image file
        formData.append("isface", isface); //put card side either the card image is front or back
        formData.append("country_code", "UGA"); //put country_code of the card image
        formData.append("card_code", card_code); //put card_code of the card image
        formData.append("passport", "false"); //if image is a passport put true else put false
        formData.append("webcam", "false"); //if image is captured from a webcam put true else if image is captured from a mobile put  false

        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);
        }
    };


    const handleCapture = async (base64: any) => {
        console.log("Capture result:", 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");
        }
    };

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

        import("accuraidscanplugin").then((Module) => {
            const IDCardPlugin = Module.default;

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

            pluginRef.current.start().then(() => {
                console.log("ID Scanner Ready");
                setReady(true);
            });
        }).catch(err => console.error("Plugin failed to load:", err));

        return () => {
            if (pluginRef.current) {
                pluginRef.current.destroy();
            }
        };
    }, []);

    return (
        <></>
    );
}
```

***

### Step 7: Usage

Import and render the component in `app/page.tsx`:

```tsx
import IDScanner from "./components/IDScanner";

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

***

### Step 8: Running the Project

```bash
npm run dev
```
