Back
DOCS
OverviewQuick StartEmbed WidgetEventsAPI ReferenceStatus PollingDocumentsRetentionBYOSExamples
OverviewQuick StartEmbed WidgetEventsAPI ReferenceStatus PollingDocumentsRetentionBYOSExamples
OverviewQuick StartEmbed WidgetEventsAPI ReferenceStatus PollingDocumentsRetentionBYOSExamples
  1. Home
  2. Docs
  3. Examples
  4. React

React Example

A complete React component that embeds the AgeEvidence verification widget, handles postMessage events, and includes a backend polling pattern.

VerificationWidget Component

This component renders the AgeEvidence iframe and exposes callback props for each verification lifecycle event. Replace pk_verify_YOUR_KEY with your publishable API key from the dashboard.

import { useState, useEffect, useCallback } from 'react';

const AGEEVIDENCE_URL = 'https://ageevidence.com';
const API_KEY = 'pk_verify_YOUR_KEY'; // Your publishable key

interface VerificationResult {
  verificationId: string;
  status: 'pending' | 'approved' | 'rejected';
}

function VerificationWidget({
  externalId,
  level = 'full_age',
  onComplete,
  onError,
  onCancel,
}: {
  externalId: string;
  level?: 'age_only' | 'full_age' | 'full_kyc';
  onComplete: (result: VerificationResult) => void;
  onError: (error: string) => void;
  onCancel?: () => void;
}) {
  const [loaded, setLoaded] = useState(false);

  const handleMessage = useCallback((event: MessageEvent) => {
    if (event.origin !== AGEEVIDENCE_URL) return;

    const { type, data } = event.data;

    switch (type) {
      case 'ae:ready':
        setLoaded(true);
        break;
      case 'ae:complete':
        onComplete({
          verificationId: data.verificationId,
          status: data.status,
        });
        break;
      case 'ae:error':
        onError(data.error);
        break;
      case 'ae:cancel':
        onCancel?.();
        break;
    }
  }, [onComplete, onError, onCancel]);

  useEffect(() => {
    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, [handleMessage]);

  const params = new URLSearchParams({
    apiKey: API_KEY,
    externalId,
    theme: 'dark',
    locale: 'en',
    parentOrigin: window.location.origin,
  });

  return (
    <div style={{ position: 'relative' }}>
      {!loaded && (
        <div style={{
          position: 'absolute',
          inset: 0,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          background: '#111',
          borderRadius: 12,
        }}>
          Loading...
        </div>
      )}
      <iframe
        src={`${AGEEVIDENCE_URL}/embed/${level}?${params}`}
        width="100%"
        height="600px"
        frameBorder="0"
        allow="camera"
        style={{
          border: 'none',
          borderRadius: 12,
          opacity: loaded ? 1 : 0,
        }}
      />
    </div>
  );
}

// Usage
function App() {
  const handleComplete = async (result: VerificationResult) => {
    console.log('Verification submitted:', result.verificationId);

    // Poll status from YOUR backend (which uses the secret key)
    const status = await checkVerificationStatus('user_123');
    if (status.verified) {
      // Grant access
    }
  };

  const handleError = (error: string) => {
    console.error('Verification error:', error);
  };

  return (
    <VerificationWidget
      externalId="user_123"
      level="full_age"
      onComplete={handleComplete}
      onError={handleError}
    />
  );
}

// Call YOUR backend proxy (never call AgeEvidence API directly from browser)
async function checkVerificationStatus(externalId: string) {
  const res = await fetch(`/api/check-verification?externalId=${externalId}`);
  return res.json();
}

Backend Polling

The checkVerificationStatus function calls your own backend, which in turn calls the AgeEvidence API with the secret key. Never expose sk_verify_ in browser code.

See the Status Polling guide for recommended polling cadence and backoff strategies.

Notes

  • The allow="camera" attribute on the iframe is required for camera access.
  • Always validate event.origin in the postMessage handler.
  • The widget is responsive and adapts to the container width.
  • Use the level prop to switch between age_only, full_age, and full_kyc.
© 2026 AgeEvidence. All rights reserved.
DocsPricingPrivacyTermsContact