> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pruva.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native SDK

> Add the Pruva verification widget to a React Native app, in Expo or bare React Native.

`@pruva/react-native` brings the verification widget into a React Native app. You get the same flow as the web widget, liveness, capture, and submission, presented in a full screen modal, with the result delivered to native callbacks.

The widget runs inside a secure WebView served from Pruva. The camera, the liveness detection, and the capture all happen in that WebView, so nothing sensitive is reimplemented in your app, and improvements to the widget reach your app without an SDK update.

<Note>
  Works in **Expo** (development and EAS builds) and **bare React Native**. It does not run in **Expo Go**, which cannot grant the native camera access a WebView needs. Use a development build.
</Note>

## Install

```bash theme={null}
npm install @pruva/react-native react-native-webview
```

`react-native-webview` is a peer dependency; install it in your app. On bare React Native iOS, run `pod install` afterwards:

```bash theme={null}
cd ios && pod install
```

## Camera permission

The widget needs the camera. The SDK grants the in-WebView camera request for you, but your app must hold the OS camera permission. This is the one platform step you cannot skip.

<Tabs>
  <Tab title="Bare React Native">
    **iOS**, in `ios/YourApp/Info.plist`:

    ```xml theme={null}
    <key>NSCameraUsageDescription</key>
    <string>We use the camera to verify your identity.</string>
    ```

    **Android**, in `android/app/src/main/AndroidManifest.xml`:

    ```xml theme={null}
    <uses-permission android:name="android.permission.CAMERA" />
    ```
  </Tab>

  <Tab title="Expo">
    In `app.json`:

    ```json theme={null}
    {
      "expo": {
        "ios": {
          "infoPlist": {
            "NSCameraUsageDescription": "We use the camera to verify your identity."
          }
        },
        "android": { "permissions": ["CAMERA"] }
      }
    }
    ```

    On managed Expo, request the OS permission before opening the widget (for example with `expo-camera`'s `requestCameraPermissionsAsync()`), then use a development build rather than Expo Go.
  </Tab>
</Tabs>

## Usage

The SDK offers two equivalent ways to open the widget: a hook, or a component.

<CodeGroup>
  ```jsx Hook theme={null}
  import { usePruvaVerify } from '@pruva/react-native';
  import { Button, View } from 'react-native';

  export default function Onboarding() {
    const { open, PruvaModal } = usePruvaVerify({
      widgetKey: 'pub_your_widget_key',
      user: { firstName: 'Ada', lastName: 'Okafor', email: 'ada@example.com' },
      onComplete: ({ reference, result }) => {
        // Confirm the outcome on your server using reference.
      },
      onClose: ({ completed }) => {
        if (!completed) {
          // user backed out before finishing
        }
      },
    });

    return (
      <View>
        <Button title="Verify your identity" onPress={open} />
        <PruvaModal />
      </View>
    );
  }
  ```

  ```jsx Component theme={null}
  import { useState } from 'react';
  import { Button, View } from 'react-native';
  import { PruvaVerify } from '@pruva/react-native';

  export default function Onboarding() {
    const [visible, setVisible] = useState(false);

    return (
      <View>
        <Button title="Verify your identity" onPress={() => setVisible(true)} />
        <PruvaVerify
          visible={visible}
          widgetKey="pub_your_widget_key"
          user={{ firstName: 'Ada', lastName: 'Okafor', email: 'ada@example.com' }}
          onComplete={({ reference, result }) => {
            // Confirm on your server using reference.
          }}
          onClose={() => setVisible(false)}
        />
      </View>
    );
  }
  ```
</CodeGroup>

The hook manages the modal's visibility for you and returns `open()`, `close()`, and a `PruvaModal` component to render once. The component version is controlled: you own the `visible` state.

## Props

<ParamField path="widgetKey" type="string" required>
  Your **public** widget key (`pub_...`). Safe to ship in an app binary.
</ParamField>

<ParamField path="visible" type="boolean">
  Controls the modal. Component only; the hook manages this.
</ParamField>

<ParamField path="user" type="object">
  `{ firstName, lastName, email }`. Optional; prefills the flow and attaches to the result.
</ParamField>

<ParamField path="baseUrl" type="string">
  Override the widget origin. Defaults to your Pruva app origin.
</ParamField>

<ParamField path="apiUrl" type="string">
  Override the API base the widget calls. Defaults to `https://api.pruva.africa`.
</ParamField>

<ParamField path="brandColor" type="string">
  Hex color for the loading spinner, so it matches your brand from the first frame.
</ParamField>

<ParamField path="debug" type="boolean">
  Show the live liveness numbers on screen, for testing.
</ParamField>

## Callbacks

<ResponseField name="onReady" type="function">
  `({ widgetType })` when the widget has loaded and is ready.
</ResponseField>

<ResponseField name="onStart" type="function">
  `()` when the user begins the flow.
</ResponseField>

<ResponseField name="onComplete" type="function">
  `({ reference, result })` when a check finishes. `reference` is the verification id; `result` is `passed`, `failed`, or `redirect`.
</ResponseField>

<ResponseField name="onError" type="function">
  `({ message })` on an error.
</ResponseField>

<ResponseField name="onClose" type="function">
  `({ completed })` once, when the modal dismisses. `completed` is `true` if a check finished, `false` if the user backed out.
</ResponseField>

## Confirm the result on your server

`onComplete` gives you a `reference` and a client side `result`. Treat your server as the source of truth: before you grant access or mark a user verified, confirm the outcome from your backend with the reference.

```bash theme={null}
curl https://api.pruva.africa/v1/verifications/THE_REFERENCE \
  -H "X-Pruva-Key: pruva_live_YOUR_KEY"
```

<Warning>
  Do not trust the client `result` alone to make a security decision. The `X-Pruva-Key` call above runs on your server with your secret key and cannot be tampered with; the in app callback can. See [Get a verification](/api-reference/get-verification).
</Warning>

## Public key, not API key

The `widgetKey` is a **public** key, built to live in a browser or an app binary. Never put a `pruva_live_...` API key in your app; that belongs on your server only. See [Authentication](/get-started/authentication).

## Troubleshooting

| Symptom | Cause and fix |
| - | - |
| Black screen where the camera should be | OS camera permission not granted. Check the Info.plist / manifest entries, and on Expo request permission before opening and use a development build, not Expo Go. |
| `react-native-webview is not installed` | Add the peer dependency, and on iOS run `pod install`. |
| Nothing happens on open | Confirm `widgetKey` is a valid `pub_...` key and the widget is active in your dashboard. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.