@mallient/react
v0.0.1-alpha.5
Published
React component library for identity and business verification — KYC and KYB flows with WebSocket-driven state, cross-device QR handoff, and theme support.
Readme
@mallient/react
React component library for identity and business verification — KYC and KYB flows with WebSocket-driven state, cross-device QR handoff, and theme support.
Table of Contents
- Installation
- Quick Start
- Import Paths
- MallientProvider
- KYC — Identity Verification
- KYB — Business Verification
- Core utilities
- TypeScript types
- Development
- Peer requirements
Installation
npm install @mallient/react
# or
yarn add @mallient/react
# or
pnpm add @mallient/reactReact 18 or 19 is required as a peer dependency.
Quick Start
import { MallientProvider, MallientKYC } from '@mallient/react'
function VerifyPage() {
return (
<MallientProvider clientId="clt_live_abc123" domain="acme.com">
<MallientKYC
onComplete={() => router.push('/dashboard')}
onFailed={(reason) => setError(reason)}
/>
</MallientProvider>
)
}// Business verification
import { MallientProvider } from '@mallient/react'
import { MallientKYB } from '@mallient/react/kyb'
function OnboardingPage() {
return (
<MallientProvider clientId="clt_live_abc123" domain="acme.com">
<MallientKYB
onSearchCompany={async (q) => fetchCompanies(q)}
onUploadDocument={async (file) => uploadDoc(file)}
onRequestKYCToken={async (uboId) => getKYCSession(uboId)}
onSubmit={async () => submitApplication()}
/>
</MallientProvider>
)
}Import Paths
The package ships separate entry points for tree-shaking. Import only what you use:
| Path | Contents |
|------|----------|
| @mallient/react | MallientProvider, MallientKYC, MallientKYB, and shared types |
| @mallient/react/kyc | All KYC components + types |
| @mallient/react/kyb | All KYB components + types |
| @mallient/react/core | Core primitives: MallientProvider, SessionProvider, StatusBadge, UploadZone, ErrorBoundary, all types |
| @mallient/react/kyc-widget | Legacy form-based KYC widget |
| @mallient/react/button | Base Button component |
MallientProvider
MallientProvider is the single root context provider for all Mallient widgets. Place it once near the top of your component tree. All MallientKYC, MallientKYB, KYCElement, and useKYC calls must be descendants.
MallientProvider props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| clientId | string | ✓ (self-managed session) | Your Mallient client identifier. Required when the provider creates sessions automatically. Omit only when supplying a pre-created sessionToken. |
| domain | string | | Domain sent to the session service. Defaults to window.location.hostname. |
| mobileVerifyBaseUrl | string | | Base URL embedded in the QR code. Defaults to the current page's origin + pathname. |
| sessionToken | string | | Pre-created session token — bypasses automatic session creation. Use this for externally-managed sessions (e.g. KYB UBO flow). |
| mobileUrl | string | | Pre-built mobile handoff URL. Used together with sessionToken. |
Self-managed session
The provider connects to the Mallient session service automatically using your clientId and domain. This is the default mode.
import { MallientProvider, MallientKYC } from '@mallient/react'
export default function VerifyPage() {
return (
<MallientProvider clientId="clt_live_abc123" domain="acme.com">
<MallientKYC
onComplete={() => router.push('/done')}
onFailed={(reason) => console.error(reason)}
/>
</MallientProvider>
)
}Pre-created session
When your backend provisions the session, pass sessionToken and mobileUrl to skip automatic session creation.
import { MallientProvider, MallientKYC } from '@mallient/react'
export default function VerifyPage() {
const { sessionToken, mobileUrl } = useVerificationSession() // your hook
return (
<MallientProvider sessionToken={sessionToken} mobileUrl={mobileUrl}>
<MallientKYC
onComplete={() => router.push('/done')}
onFailed={(reason) => console.error(reason)}
/>
</MallientProvider>
)
}KYC — Identity Verification
MallientKYC
The identity verification widget. Renders a styled card shell around the KYC flow.
Requires <MallientProvider> above it in the tree to supply the session context.
The widget handles:
- Live status updates via WebSocket
- Cross-device QR handoff (desktop shows QR → mobile captures)
- Final result display with pass/fail per check
Basic usage
import { MallientProvider, MallientKYC } from '@mallient/react'
export default function VerifyPage() {
return (
<MallientProvider clientId="clt_live_abc123" domain="acme.com">
<MallientKYC
onComplete={() => {
router.push('/dashboard')
}}
onFailed={(reason) => {
console.error('Verification failed:', reason)
}}
/>
</MallientProvider>
)
}How the session flow works
MallientProvider (creates/connects session)
│
▼
<MallientKYC>
│
├─ WebSocket connects → status: initializing → awaiting_mobile
│
├─ User scans QR / opens mobileUrl on phone
│ └─ status: mobile_connected
│
├─ Capture runs on device
│ └─ status: in_progress
│
└─ Backend sends result via WebSocket
├─ verification.complete → status: complete → onComplete()
├─ verification.failed → status: failed → onFailed(reason)
└─ review.escalated → status: manual_reviewMallientKYC props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| onComplete | () => void | | Called when verification succeeds |
| onFailed | (reason: string) => void | | Called when verification fails or session expires |
| theme | KYCTheme | | Visual theme overrides (see Theming) |
| title | string | | Widget header title (default: 'Identity Verification') |
| loadingContent | ReactNode | | Custom content shown while the session initializes. Defaults to a spinner. |
Theming
All components are styled with CSS custom properties. Pass a theme object to override them on a per-instance basis:
<MallientProvider clientId="clt_live_abc123" domain="acme.com">
<MallientKYC
theme={{
primaryColor: '#6366f1', // --ml-primary
borderRadius: '12px', // --ml-radius
fontFamily: 'Inter, sans-serif', // --ml-font
backgroundColor: '#ffffff', // --ml-bg
textColor: '#111827', // --ml-text
}}
onComplete={() => router.push('/done')}
/>
</MallientProvider>For global defaults across all components, set the custom properties in your own CSS:
:root {
--ml-primary: #6366f1;
--ml-radius: 10px;
--ml-font: 'Inter', sans-serif;
}Individual KYC components
Import from @mallient/react/kyc to compose your own layout. All components must be inside <MallientProvider>.
import {
KYCElement,
PendingState,
SuccessState,
FailedState,
ManualReviewState,
QRDisplay,
StatusBadge,
CrossDeviceHandoff,
VerificationStatus,
ResultSummary,
SessionStatus,
} from '@mallient/react/kyc'KYCElement
The unstyled KYC flow — use this with your own layout instead of MallientKYC:
import { MallientProvider } from '@mallient/react'
import { KYCElement } from '@mallient/react/kyc'
<MallientProvider clientId="clt_live_abc123" domain="acme.com">
<div className="my-card">
<KYCElement
onComplete={() => router.push('/done')}
onFailed={(reason) => setError(reason)}
/>
</div>
</MallientProvider>StatusBadge
Displays a colour-coded badge for any session status:
import { StatusBadge } from '@mallient/react/core'
<StatusBadge status="in_progress" />
<StatusBadge status="complete" />QRDisplay
Renders the mobile handoff QR code using an HTML5 canvas:
import { QRDisplay } from '@mallient/react/kyc'
<QRDisplay
url="https://verify.mallient.com/mobile/abc123"
size={200} // optional, pixels (default: 200)
hint="Scan with your phone" // optional caption
/> │ └─ status: mobile_connected
│
├─ Capture runs on device
│ └─ status: in_progress
│
└─ Backend sends result via WebSocket
├─ verification.complete → status: complete → onComplete(result)
├─ verification.failed → status: failed → onFailed(reason)
└─ review.escalated → status: manual_review
#### MallientKYC props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `sessionToken` | `string` | ✓ | Session token from your backend |
| `mobileUrl` | `string` | ✓ | Mobile capture deep link from your backend |
| `onComplete` | `(result: VerificationResult) => void` | | Called when verification succeeds |
| `onFailed` | `(reason: string) => void` | | Called when verification fails or session expires |
| `theme` | `KYCTheme` | | Visual theme overrides (see [Theming](#theming)) |
| `wsUrl` | `string` | | Override the WebSocket server URL (default: `wss://verify.mallient.com/ws`) |
| `title` | `string` | | Widget header title (default: `'Identity Verification'`) |
### Theming
All components are styled with CSS custom properties. Pass a `theme` object to override them on a per-instance basis:
```tsx
<MallientKYC
sessionToken={token}
mobileUrl={url}
theme={{
primaryColor: '#6366f1', // --ml-primary
borderRadius: '12px', // --ml-radius
fontFamily: 'Inter, sans-serif', // --ml-font
backgroundColor: '#ffffff', // --ml-bg
textColor: '#111827', // --ml-text
}}
/>For global defaults across all components, set the custom properties in your own CSS:
:root {
--ml-primary: #6366f1;
--ml-radius: 10px;
--ml-font: 'Inter', sans-serif;
}Individual KYC components
Import from @mallient/react/kyc to compose your own layout:
import {
PendingState,
SuccessState,
FailedState,
ManualReviewState,
QRDisplay,
StatusBadge,
CrossDeviceHandoff,
VerificationStatus,
ResultSummary,
SessionStatus,
} from '@mallient/react/kyc'SessionProvider
Wrap any subtree that needs access to the live WebSocket session state:
import { SessionProvider, useSession } from '@mallient/react/core'
function MyFlow({ token, mobileUrl }: { token: string; mobileUrl: string }) {
return (
<SessionProvider token={token}>
<MyCustomUI mobileUrl={mobileUrl} />
</SessionProvider>
)
}
function MyCustomUI({ mobileUrl }: { mobileUrl: string }) {
const { state } = useSession()
// state.status: 'idle' | 'initializing' | 'awaiting_mobile' | 'mobile_connected'
// | 'in_progress' | 'manual_review' | 'complete' | 'failed' | 'expired'
return <p>Status: {state.status}</p>
}StatusBadge
Displays a colour-coded badge for any session status:
import { StatusBadge } from '@mallient/react/core'
<StatusBadge status="in_progress" />
<StatusBadge status="complete" />QRDisplay
Renders the mobile handoff QR code using an HTML5 canvas:
import { QRDisplay } from '@mallient/react/kyc'
<QRDisplay
url="https://verify.mallient.com/mobile/abc123"
size={200} // optional, pixels (default: 200)
hint="Scan with your phone" // optional caption
/>KYB — Business Verification
MallientKYB
A 6-step wizard for business onboarding. Requires <MallientProvider> above it in the tree.
Steps:
- Company search — look up by name or registration number (or enter manually)
- Business details — legal name, registration number, jurisdiction, address, business type
- Document upload — drag-and-drop articles of incorporation, licences, etc.
- Beneficial owners — add UBOs (≥ 25% ownership) and trigger KYC per person
- Review — summary before submission
- Submitted — confirmation screen
Basic usage
import { MallientProvider } from '@mallient/react'
import { MallientKYB } from '@mallient/react/kyb'
import type { CompanySearchResult, UploadedDocument } from '@mallient/react/core'
export default function BusinessOnboarding() {
return (
<MallientProvider clientId="clt_live_abc123" domain="acme.com">
<MallientKYB
onSearchCompany={searchCompanies}
onUploadDocument={uploadDocument}
onRequestKYCToken={getUBOKYCSession}
onSubmit={submitApplication}
/>
</MallientProvider>
)
}
// Search your company registry API
async function searchCompanies(query: string): Promise<CompanySearchResult[]> {
const res = await fetch(`/api/companies?q=${encodeURIComponent(query)}`)
return res.json()
}
// Upload a document and return metadata
async function uploadDocument(file: File): Promise<UploadedDocument> {
const form = new FormData()
form.append('file', file)
const res = await fetch('/api/documents', { method: 'POST', body: form })
return res.json()
// Must return: { id, name, type, size, uploadedAt, url? }
}
// Provision a KYC session for a beneficial owner
async function getUBOKYCSession(uboId: string) {
const res = await fetch('/api/ubo-kyc', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ uboId }),
})
return res.json()
// Must return: { sessionToken: string, mobileUrl: string }
}
// Final submission
async function submitApplication() {
await fetch('/api/kyb/submit', { method: 'POST' })
}MallientKYB props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| onSearchCompany | (query: string) => Promise<CompanySearchResult[]> | | Registry search. If omitted, users can only enter a name manually. |
| onUploadDocument | (file: File) => Promise<UploadedDocument> | | Called for each dropped file. If omitted, the upload zone is disabled. |
| onRequestKYCToken | (uboId: string) => Promise<{ sessionToken: string; mobileUrl: string }> | | Called when a UBO is added. If omitted, UBOs are listed but KYC is not triggered. |
| onSubmit | () => Promise<void> | | Called when the user clicks Submit. |
| theme | KYCTheme | | Same theme object as MallientKYC. |
| title | string | | Wizard header title (default: 'Business Verification'). |
Company step — manual entry
If a company is not found in the registry (or no onSearchCompany is provided), users can type a name and click Use this name to continue. The wizard advances with status: 'Unverified' and the typed name pre-filled in the business details form.
Individual KYB components
Import from @mallient/react/kyb to compose your own step layout:
import {
KYBSessionProvider,
useKYBSession,
CompanySearch,
BusinessDetailsForm,
DocumentUpload,
DocumentPreview,
UploadProgress,
UBOCollection,
UBOForm,
UBOKYCTrigger,
SubmissionStatus,
} from '@mallient/react/kyb'useKYBSession
Reads and dispatches to the KYB state machine. Must be used inside <KYBSessionProvider>:
import { KYBSessionProvider, useKYBSession } from '@mallient/react/kyb'
function MyStep() {
const { state, dispatch } = useKYBSession()
return (
<button onClick={() => dispatch({ type: 'navigate', step: 'ubo_collection' })}>
Skip to owners
</button>
)
}
function App() {
return (
<KYBSessionProvider>
<MyStep />
</KYBSessionProvider>
)
}KYB state machine actions
| Action | Effect |
|--------|--------|
| { type: 'company.selected', company } | Advance to business_details |
| { type: 'details.submitted', details } | Advance to document_upload |
| { type: 'documents.uploaded', documents } | Advance to ubo_collection |
| { type: 'ubos.updated', ubos } | Update UBO list (stays on current step) |
| { type: 'ubo.kyc.complete', uboId, result } | Mark UBO KYC as complete |
| { type: 'ubo.kyc.failed', uboId } | Mark UBO KYC as failed |
| { type: 'review.start' } | Advance to review |
| { type: 'submit.start' } | Set submitting: true |
| { type: 'submit.complete' } | Advance to submitted |
| { type: 'submit.failed', reason } | Set error message |
| { type: 'navigate', step } | Jump to any step |
Core utilities
Import from @mallient/react/core:
import {
MallientProvider, // single root provider for all Mallient widgets
SessionProvider, // low-level WebSocket session context (used by MallientProvider)
useSession, // consume session state inside a MallientProvider
useWebSocket, // low-level WebSocket hook with auto-reconnect
StatusBadge, // status pill component
UploadZone, // drag-and-drop file target
ErrorBoundary, // class component error boundary
injectStyles, // CSS-in-JS helper (used internally)
buildThemeVars, // converts KYCTheme → CSS custom property map
} from '@mallient/react/core'UploadZone
import { UploadZone } from '@mallient/react/core'
<UploadZone
accept=".pdf,.jpg,.png"
multiple
hint="PDF, JPG or PNG · Max 10 MB"
onFiles={(files: File[]) => handleFiles(files)}
/>ErrorBoundary
import { ErrorBoundary } from '@mallient/react/core'
<ErrorBoundary
fallback={<p>Something went wrong.</p>}
onError={(err, info) => logError(err, info)}
>
<MallientKYC ... />
</ErrorBoundary>TypeScript types
All types are exported from @mallient/react/core:
import type {
// KYC
VerificationResult, // { sessionId, status, checks, completedAt }
SessionState, // discriminated union of all KYC statuses
WSMessage, // WebSocket message union
KYCTheme, // theme override object
// KYB
CompanySearchResult, // { companyNumber, companyName, jurisdiction, status, ... }
BusinessDetails, // { legalName, registrationNumber, jurisdiction, address, businessType }
UploadedDocument, // { id, name, type, size, uploadedAt, url? }
UBO, // { id, firstName, lastName, ownershipPercentage, kycStatus, ... }
KYBState, // full KYB wizard state
KYBAction, // all dispatchable actions
KYBStep, // 'company_search' | 'business_details' | ... | 'submitted'
} from '@mallient/react/core'Development
# Install dependencies
npm install
# Start the component demo (hot-reload dev server)
npm run dev
# Type-check without emitting
npm run typecheck
# Lint
npm run lint
# Build the distributable package
npm run buildThe dev server at http://localhost:5173 runs an interactive demo with three tabs:
- KYB Flow — fully interactive wizard with mock handlers
- KYC States — all verification states rendered without a live backend
- Legacy Components — original
KYCWidgetform andButton
Peer requirements
| Package | Version |
|---------|---------|
| react | ^18 or ^19 |
| react-dom | ^18 or ^19 |
License
MIT
