Skip to main content

React Native

Two integration paths are available. The npm package is recommended.

npm packageRaw WebView
Setupnpm installManual
Modal managementBuilt-inYou manage
TypeScript typesIncludedNone

npm package

Installation

npm install @surtai/faceguard-rn react-native-webview
cd ios && pod install

Platform requirements

iOS - add to Info.plist:

Info.plist
<key>NSCameraUsageDescription</key>
<string>FaceGuard needs camera access for face verification</string>

Android - add to AndroidManifest.xml:

AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />

Setup

FaceGuard.init() and FaceGuard.verify() require FaceGuardProvider at your app root. FaceGuardView does not.

App.tsx
import { FaceGuardProvider } from '@surtai/faceguard-rn';

export default function App() {
return (
<FaceGuardProvider>
{/* rest of your app */}
</FaceGuardProvider>
);
}

Usage

Promise-based (requires provider):

import { FaceGuard } from '@surtai/faceguard-rn';

const result = await FaceGuard.verify({ token: 'PORTAL_TOKEN' });

switch (result.status) {
case 'approved':
console.log('Verified', result.confidence);
break;
case 'rejected':
console.log('Failed', result.confidence);
break;
case 'canceled':
console.log('User canceled');
break;
case 'error':
console.log('Error', result.error);
break;
}

Callback-based (requires provider):

const session = FaceGuard.init({
token: 'PORTAL_TOKEN',
onSuccess: (r) => console.log('approved', r.confidence),
onFailed: (r) => console.log('rejected', r.confidence),
onCancel: () => console.log('canceled'),
onError: (e) => console.log('error', e.message),
});

// close early if needed
session.destroy();

Declarative component (no provider needed):

import { FaceGuardView } from '@surtai/faceguard-rn';
import { Modal } from 'react-native';

<Modal visible={isOpen} onRequestClose={() => setIsOpen(false)}>
<FaceGuardView
token="PORTAL_TOKEN"
style={{ flex: 1 }}
onSuccess={() => setIsOpen(false)}
onCancel={() => setIsOpen(false)}
/>
</Modal>
React Navigation
import { useNavigation } from '@react-navigation/native';
import { FaceGuard } from '@surtai/faceguard-rn';

function VerifyButton({ token }: { token: string }) {
const navigation = useNavigation();

const handleVerify = async () => {
const result = await FaceGuard.verify({ token });

if (result.status === 'approved') {
navigation.replace('Dashboard', { verified: true });
} else if (result.status === 'rejected') {
navigation.replace('VerificationFailed');
} else if (result.status === 'canceled') {
navigation.goBack();
}
};

return <Button title="Verify" onPress={handleVerify} />;
}

Options

OptionTypeRequiredDefaultDescription
tokenstringYesPortal JWT from your backend
baseUrlstringNohttps://faceguard.surt.comOverride for non-prod environments
langstringNoDevice default'en', 'es', 'pt', or 'de'

Callbacks (FaceGuard.init)

CallbackPayloadDescription
onReadyFaceGuard loaded
onSuccess{ confidence: number }Verification passed (0-100)
onFailed{ confidence: number }Verification failed (0-100)
onCancelUser closed FaceGuard
onError{ message: string }Something went wrong

Result (FaceGuard.verify)

interface FaceGuardVerifyResult {
status: 'approved' | 'rejected' | 'canceled' | 'error';
confidence?: number; // 0-100, present for approved/rejected
error?: string; // present for error status
}
One-shot per token

The backend closes the session after the first attempt. If the user fails, your backend must issue a new portal token before calling verify() again.

Raw WebView

Use this if you want full control over the WebView lifecycle.

npm install react-native-webview
FaceGuardScreen.tsx
import React from 'react';
import { SafeAreaView, StyleSheet } from 'react-native';
import { WebView } from 'react-native-webview';

interface FaceGuardProps {
token: string;
onApproved: (confidence: number) => void;
onRejected: (confidence: number) => void;
onCanceled: () => void;
onError: (message: string) => void;
}

export function FaceGuardScreen({
token, onApproved, onRejected, onCanceled, onError
}: FaceGuardProps) {
const uri = `https://faceguard.surt.com/intro?token=${encodeURIComponent(token)}`;

const handleMessage = (event: { nativeEvent: { data: string } }) => {
try {
const data = JSON.parse(event.nativeEvent.data);

if (data.action === 'close') {
switch (data.reason) {
case 'approved':
case 'bypass_active':
onApproved(data.confidence ?? 0);
break;
case 'rejected':
onRejected(data.confidence ?? 0);
break;
case 'canceled':
onCanceled();
break;
case 'error':
case 'no_base_photo':
onError(data.error ?? data.reason);
break;
}
}
} catch {
// ignore non-JSON messages
}
};

return (
<SafeAreaView style={styles.container}>
<WebView
source={{ uri }}
onMessage={handleMessage}
mediaPlaybackRequiresUserAction={false}
allowsInlineMediaPlayback={true}
mediaCapturePermissionGrantType="grant"
javaScriptEnabled={true}
domStorageEnabled={true}
style={styles.webview}
/>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0D141A' },
webview: { flex: 1 },
});

iOS - add to Info.plist:

Info.plist
<key>NSCameraUsageDescription</key>
<string>FaceGuard needs camera access for face verification</string>

Android - add to AndroidManifest.xml and handle the permission request:

AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
Android camera permission
<WebView
{/* ...other props */}
onPermissionRequest={(request) => {
if (request.resources.includes('android.webkit.resource.VIDEO_CAPTURE')) {
request.grant(request.resources);
}
}}
/>

Language

Append &lang=es (or pt, de) to the URL:

const uri = `https://faceguard.surt.com/intro?token=${token}&lang=es`;
Dark background

Always set backgroundColor: '#0D141A' on the container to avoid a white flash while the WebView loads.