Web SDK (React)
The Web SDK is a drop-in React component that renders the full verification UI — ID selection, document scan, and active liveness — and calls the verification API for you. It uses a publishable (pk_) key and detects the environment automatically from the key prefix (pk_test_* → sandbox, pk_live_* → production). For shared concepts — supported countries, branding, results, and errors — see Client SDKs.
Install
pnpm add @myazahq/kyc-sdk-reactyarn add @myazahq/kyc-sdk-reactnpm install @myazahq/kyc-sdk-reactUsage
<MyazaKYC /> renders a "Verify Identity" button plus the full modal flow. The trigger is a real <button> — pass children to relabel it, className to restyle it, or any other button attribute (disabled, type, aria-*, …). See Customizing the trigger button. Import the bundled stylesheet once, anywhere in your app.
"use client";
import { MyazaKYC } from "@myazahq/kyc-sdk-react";
import "@myazahq/kyc-sdk-react/styles.css";
export default function VerifyButton() {
return (
<MyazaKYC
apiKey="pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
country="NG"
idTypes={["bvn", "drivers-license", "passport"]}
userData={{ firstName: "Jane", lastName: "Doe" }}
enableSelfie
enableDocumentCapture
enableLiveness
showThemeToggle
appearance={{ primaryColor: "#5645F5", companyName: "Myaza", logo: "default", theme: "light" }}
consent={{ title: "Welcome, {firstName}", description: "A quick check to confirm it's really you." }}
success={{ title: "You're all set, {firstName}!", description: "We'll email you once your verification is reviewed." }}
metadata={{ requestId: "order_1001", userId: "user_42" }}
onSubmit={(submission) => {
// The verification was created; status is always 'pending'.
// Reconcile the final result on your backend via webhook or a secret-key
// GET /verifications/:id call (never from the client).
console.log("submitted", submission.verificationId);
}}
onError={(err) => console.error(err)}
onClose={() => console.log("closed")}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Required. Sent as Authorization: Bearer. The environment is derived from the key prefix (pk_test_* → sandbox, pk_live_* → production); an unrecognized prefix throws. |
workflowId | string | — | Run a published workflow (wf_…) built in the dashboard. The SDK fetches its configuration and uses it as the source of truth — workflow config wins over overlapping props. Makes country optional. See Configure with a workflow. |
country | 'NG' | 'GH' | 'KE' | 'ZA' | 'CI' | string | — | Required unless workflowId is set. Country whose ID types are offered. Any ISO-2 country works — the org's grants are enforced server-side. |
livenessMode | 'gestures' | 'flash' | 'both' | 'gestures' | How the liveness step proves presence: randomized gestures, the screen-flash sequence, or both. Usually set by a workflow. |
deviceIntelligence | boolean | true | Collect device + IP fraud signals (Device Intelligence). |
countries | Array<{ country, idTypes? }> | — | Multi-region. List more than one country and the flow opens with a country-select step; the picked country's idTypes win. Usually set by a workflow. |
idTypes | IdType[] | all enabled for org | Subset of ID types to offer; must be valid for country. |
userData | { firstName?, lastName?, dateOfBirth? } | — | Pre-fills the user's details; fields provided here aren't asked again. |
enableSelfie | boolean | true | Capture a selfie during liveness. |
enableDocumentCapture | boolean | true | Enable the document-scan step for document IDs. |
allowDocumentUpload | boolean | true | Allow picking a document photo from the device (gallery / drag-and-drop) instead of the camera. false hides every "upload instead" affordance — except on the camera-permission-denied screen, where it stays as an escape hatch. |
enableLiveness | boolean | true | Run the liveness challenge step. The server can still disable it per ID type. |
voiceGuidance | boolean | { enabled?, language? } | true | Spoken liveness instructions (accessibility, TTS output — no microphone). false mutes it; { language: 'fr-FR' } sets the voice language. |
showThemeToggle | boolean | true | Show a light/dark toggle inside the modal header. Set false to hide it — the flow then stays on appearance.theme and the user can't switch it. |
fullScreen | boolean | false | Force the flow to render full screen on every device (desktop drops the centered modal; the expand/collapse control is hidden). |
disableClose | boolean | false | Hide the close (X) button and block all user dismissal (backdrop click, Escape, mobile swipe-down). The flow can then only be closed programmatically via the useMyazaKYC() hook's close(). The terminal "Submitted" step is non-dismissible regardless. |
deviceHandoff | boolean | true | On desktop, show a "continue on your phone" screen (QR code + copyable link) before the flow starts — useful when the computer has no webcam. The user can still continue on the current device; when they finish on their phone, the desktop completes automatically and fires onSubmit. Set false to disable. No effect on mobile/touch devices. |
appearance | KYCAppearance | brand defaults | Brand & theme the modal — colors, logo, light/dark. See Branding & theming. |
consent | KYCConsentContent | built-in copy | Override the consent/welcome screen title and description. See Consent screen copy. |
success | KYCSuccessContent | built-in copy | Override the success/submitted screen title and description. See Success screen copy. |
metadata | Record<string, string> | — | Forwarded with the verify request (include your requestId). |
onStart | () => void | — | Called when the flow opens. |
onStepChange | (step: KYCStep) => void | — | Called on each step transition. |
onSubmit | (submission: KYCSubmission) => void | — | Called when the server accepts the verification. status is always 'pending'. |
onClose | () => void | — | Called when the user closes the flow. |
onError | (error: KYCError) => void | — | Called for technical errors only — receives a typed KYCError (a real Error with a code). Verification outcomes never come through here. See Errors. |
children | ReactNode | Verify Identity | Trigger button label/content. Defaults to Verify with {companyName} when companyName is set, else Verify Identity. |
className | string | — | Trigger button classes. Merged via tailwind-merge, so your classes override the built-in styling. |
| other button attrs | ButtonHTMLAttributes | — | Any standard <button> attribute (disabled, type, aria-*, style, …) is forwarded. onClick is reserved by the SDK. |
Configure with a workflow
Instead of spelling out country, idTypes, and every step toggle in code, you can build the flow once in the dashboard as a workflow and mount it by id:
<MyazaKYC
apiKey="pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
workflowId="wf_AbC123dEf456"
userId="user_42"
metadata={{ requestId: "order_1001" }}
onSubmit={(s) => console.log(s.verificationId)}
/>The workflow's configuration wins over any overlapping props, so the country, ID types, capture steps, branding, and copy all come from the dashboard. Only runtime data — userId, userData, metadata, and your callbacks — stays in code.
A workflow is also the recommended way to turn on the extra capture steps — contact OTP (emailVerification / phoneVerification), proof of address (proofOfAddress), NFC chip (nfc), questionnaire, and business (KYB) verification (subjectType: 'business' + business) — and to attach server-side decisioning. These are accepted as SDK props too, but configuring them on the workflow means you compose the checks once and change them by re-publishing — no redeploy. See Workflows.
Customizing the trigger button
<MyazaKYC /> renders a real <button>. In addition to the config props above it accepts standard button attributes (the props type is exported as MyazaKYCProps), so you can treat it like any other button:
<MyazaKYC
{...config}
className="w-full rounded-full bg-black px-6 text-white"
disabled={!ready}
>
Start verification
</MyazaKYC>childrensets the label (falls back toVerify with {companyName}/Verify Identity).classNameis merged throughtailwind-merge, so your classes win over the defaults.styleis merged on top of the SDK's injected theme variables, so theming still applies.onClickis owned by the SDK (it opens the modal) and can't be overridden. For a fully custom trigger element, use theuseMyazaKYC()hook below.
Programmatic control
For a custom trigger instead of the built-in button, wrap your tree in KYCProvider and drive the flow with the useMyazaKYC() hook:
import { KYCProvider, useMyazaKYC } from "@myazahq/kyc-sdk-react";
function Trigger() {
const { open, close, isOpen, currentStep } = useMyazaKYC({
apiKey: "pk_test_…",
country: "NG",
onSubmit: (s) => console.log(s.verificationId),
});
return <button onClick={open} disabled={isOpen}>Verify ({currentStep ?? "idle"})</button>;
}
export default () => (
<KYCProvider>
<Trigger />
</KYCProvider>
);The flow advances through KYCStep values, reported via onStepChange / currentStep. The core path is consent → id-type → (id-input for number-only IDs, or document-capture) → liveness → submitted. Steps enabled by a workflow slot in automatically — email-verification / phone-verification (right after consent), country-select (multi-region), proof-of-address, questionnaire, and the KYB steps (business-details, business-key-people, business-documents, applicant-role).