React Integration

Integrate the VECU IDV Web SDK into your React applications with proper lifecycle management.

Basic Integration

import { useEffect, useRef } from 'react';
import { createVecuIDVSDK } from 'vec-idp-web-sdk';

function VerificationComponent({ customerInfo }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const cleanupRef = useRef<(() => void) | null>(null);

  useEffect(() => {
    if (!customerInfo || !containerRef.current) return;

    const sdk = createVecuIDVSDK({
      deploymentStage: 'sandbox',
      bearerToken: process.env.REACT_APP_BEARER_TOKEN,
      debug: process.env.NODE_ENV === 'development',
      logLevel: 'info',
    });

    sdk
      .startVerificationWithCustomer(containerRef.current, {
        customerInfo,
        referenceId: `customer-${Date.now()}`,
        mode: 'embedded',
        onProgress: event => {
          console.log(`Progress: ${event.step} (${event.percentage}%)`);
        },
        onSuccess: result => {
          console.log('Verification completed!', result);
        },
        onError: error => {
          console.error('Verification failed:', error);
        },
        config: {
          qrCode: true,
        },
      })
      .then(cleanup => {
        cleanupRef.current = cleanup;
      });

    return () => {
      cleanupRef.current?.();
      sdk.destroy();
    };
  }, [customerInfo]);

  return <div ref={containerRef} style={{ minHeight: '600px' }} />;
}

Custom Hook

Create a reusable hook for verification:

import { useEffect, useRef, useState, useCallback } from 'react';
import { createVecuIDVSDK, VecuIDVSDK } from 'vec-idp-web-sdk';

interface UseVerificationOptions {
  onSuccess?: (result: any) => void;
  onError?: (error: Error) => void;
  onProgress?: (event: { step: string; percentage: number }) => void;
}

interface CustomerInfo {
  firstName: string;
  lastName: string;
  email?: string;
  phone?: string;
  address?: {
    line1: string;
    locality: string;
    majorAdminDivision: string;
    country: string;
    postalCode: string;
    type: string;
  };
}

export function useVerification(options: UseVerificationOptions = {}) {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  const [result, setResult] = useState<any>(null);

  const sdkRef = useRef<VecuIDVSDK | null>(null);
  const cleanupRef = useRef<(() => void) | null>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  // Initialize SDK
  useEffect(() => {
    sdkRef.current = createVecuIDVSDK({
      deploymentStage:
        process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',
      bearerToken: process.env.REACT_APP_BEARER_TOKEN!,
      debug: process.env.NODE_ENV === 'development',
    });

    return () => {
      cleanupRef.current?.();
      sdkRef.current?.destroy();
    };
  }, []);

  const startVerification = useCallback(
    async (customerInfo: CustomerInfo) => {
      if (!sdkRef.current || !containerRef.current) return;

      setIsLoading(true);
      setError(null);
      setResult(null);

      try {
        cleanupRef.current = await sdkRef.current.startVerificationWithCustomer(
          containerRef.current,
          {
            customerInfo,
            referenceId: `ref-${Date.now()}`,
            onProgress: options.onProgress,
            onSuccess: res => {
              setResult(res);
              setIsLoading(false);
              options.onSuccess?.(res);
            },
            onError: err => {
              setError(err);
              setIsLoading(false);
              options.onError?.(err);
            },
            config: {
              qrCode: true,
            },
          }
        );
      } catch (err) {
        setError(err as Error);
        setIsLoading(false);
      }
    },
    [options]
  );

  const cancelVerification = useCallback(() => {
    cleanupRef.current?.();
    setIsLoading(false);
  }, []);

  return {
    containerRef,
    startVerification,
    cancelVerification,
    isLoading,
    error,
    result,
  };
}

Using the Hook

function VerificationPage() {
  const {
    containerRef,
    startVerification,
    cancelVerification,
    isLoading,
    error,
    result,
  } = useVerification({
    onSuccess: result => {
      console.log('Success!', result);
      // Navigate to success page
    },
    onError: error => {
      console.error('Error:', error);
    },
  });

  const handleStart = () => {
    startVerification({
      firstName: 'John',
      lastName: 'Doe',
      email: 'john@example.com',
    });
  };

  return (
    <div>
      <h1>Identity Verification</h1>

      {error && <div className='error'>Error: {error.message}</div>}

      {result && <div className='success'>Verification Complete!</div>}

      <div ref={containerRef} style={{ minHeight: '600px' }} />

      <div className='controls'>
        <button onClick={handleStart} disabled={isLoading}>
          {isLoading ? 'Verifying...' : 'Start Verification'}
        </button>
        {isLoading && <button onClick={cancelVerification}>Cancel</button>}
      </div>
    </div>
  );
}

Next.js Integration

For Next.js applications, ensure the SDK is only loaded on the client:

'use client';

import { useEffect, useRef } from 'react';
import dynamic from 'next/dynamic';

function VerificationComponent({ customerInfo }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const cleanupRef = useRef<(() => void) | null>(null);

  useEffect(() => {
    // Dynamic import to avoid SSR issues
    import('vec-idp-web-sdk').then(({ createVecuIDVSDK }) => {
      if (!containerRef.current) return;

      const sdk = createVecuIDVSDK({
        deploymentStage: 'sandbox',
        bearerToken: process.env.NEXT_PUBLIC_BEARER_TOKEN!,
        debug: true,
      });

      sdk
        .startVerificationWithCustomer(containerRef.current, {
          customerInfo,
          referenceId: `ref-${Date.now()}`,
          onSuccess: result => {
            console.log('Success!', result);
          },
          onError: error => {
            console.error('Error:', error);
          },
        })
        .then(cleanup => {
          cleanupRef.current = cleanup;
        });

      return () => {
        cleanupRef.current?.();
        sdk.destroy();
      };
    });
  }, [customerInfo]);

  return <div ref={containerRef} style={{ minHeight: '500px' }} />;
}

// Export with no SSR
export default dynamic(() => Promise.resolve(VerificationComponent), {
  ssr: false,
});

Error Boundary

Wrap the verification component in an error boundary:

import { Component, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error?: Error;
}

class VerificationErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Verification error:', error, errorInfo);
    // Log to error tracking service
  }

  render() {
    if (this.state.hasError) {
      return (
        this.props.fallback || (
          <div className='error-fallback'>
            <h2>Verification Error</h2>
            <p>Something went wrong. Please try again.</p>
            <button onClick={() => this.setState({ hasError: false })}>
              Retry
            </button>
          </div>
        )
      );
    }

    return this.props.children;
  }
}

// Usage
function App() {
  return (
    <VerificationErrorBoundary>
      <VerificationComponent customerInfo={customerInfo} />
    </VerificationErrorBoundary>
  );
}

Lazy Loading

Load the SDK only when needed to reduce initial bundle size:

import { lazy, Suspense } from 'react';

const VerificationComponent = lazy(() => import('./VerificationComponent'));

function App() {
  const [showVerification, setShowVerification] = useState(false);

  return (
    <div>
      <button onClick={() => setShowVerification(true)}>
        Start Verification
      </button>

      {showVerification && (
        <Suspense fallback={<div>Loading verification...</div>}>
          <VerificationComponent customerInfo={customerInfo} />
        </Suspense>
      )}
    </div>
  );
}

TypeScript Types

The SDK includes TypeScript definitions. Import types as needed:

import {
  createVecuIDVSDK,
  VecuIDVSDKConfig,
  VerificationOptions,
  CustomerInfo,
  VerificationResult,
} from 'vec-idp-web-sdk';

const config: VecuIDVSDKConfig = {
  deploymentStage: 'sandbox',
  bearerToken: 'your-token',
  debug: true,
};

const sdk = createVecuIDVSDK(config);

Best Practices

  1. Always cleanup - Call the cleanup function when unmounting or when verification completes
  2. Use refs - Store SDK instance and cleanup function in refs to avoid re-initialization
  3. Handle errors - Implement proper error boundaries and error handling
  4. Lazy load - Only load the SDK when verification is needed
  5. Environment variables - Store tokens in environment variables, never in code

Next Steps