Skip to main content

React integration: reusable components

This page provides production-ready wrapper components for the Weavr SDK. For the underlying patterns these components are built on, see Component patterns.

For larger applications, organize your Weavr components:

src/
├── components/
│ └── weavr/
│ ├── hooks/
│ │ ├── useWeavrForm.ts
│ │ ├── useWeavrSpan.ts
│ │ ├── useWeavrKyc.ts
│ │ └── index.ts
│ ├── input-tokenize/
│ │ ├── WeavrPasswordInput.tsx
│ │ ├── WeavrPasscodeInput.tsx
│ │ └── index.ts
│ ├── authenticated-display/
│ │ ├── WeavrCardNumber.tsx
│ │ ├── WeavrCvv.tsx
│ │ ├── WeavrCardPin.tsx
│ │ └── index.ts
│ ├── authenticated-input/
│ │ ├── WeavrCardPinCapture.tsx
│ │ └── index.ts
│ ├── verification/
│ │ ├── WeavrKyc.tsx
│ │ ├── WeavrKyb.tsx
│ │ ├── WeavrDirectorKyc.tsx
│ │ └── index.ts
│ └── index.ts
├── context/
│ └── WeavrAuthContext.tsx
└── ...

Reusable wrapper components

WeavrPasswordInput

import React, {
forwardRef,
useImperativeHandle,
useEffect,
useRef,
useState,
} from "react";

interface WeavrPasswordInputProps {
placeholder?: string;
className?: string;
onReady?: () => void;
onError?: (error: string) => void;
}

export interface WeavrPasswordInputRef {
tokenize: () => Promise<string>;
}

const WeavrPasswordInput = forwardRef<
WeavrPasswordInputRef,
WeavrPasswordInputProps
>(({ placeholder = "Enter password", className, onReady, onError }, ref) => {
const formRef = useRef<any>(null);
const inputRef = useRef<any>(null);
const [isReady, setIsReady] = useState(false);
const containerId = useRef(`weavr-password-${Date.now()}`);

useEffect(() => {
if (typeof window.OpcUxSecureClient === "undefined") {
onError?.("Weavr SDK not loaded");
return;
}

const form = window.OpcUxSecureClient.form();
const input = form.input("password", "password", {
placeholder,
style: {
base: { color: "#333", fontSize: "16px" },
invalid: { color: "#dc3545" },
},
});

formRef.current = form;
inputRef.current = input;
input.mount(containerId.current);

input.on("ready", () => {
setIsReady(true);
onReady?.();
});

return () => {
try {
form.destroy();
} catch (e) {
console.warn("Error destroying:", e);
}
};
}, [placeholder, onReady, onError]);

useImperativeHandle(
ref,
() => ({
tokenize: () =>
new Promise((resolve, reject) => {
if (!formRef.current) {
reject(new Error("Form not initialized"));
return;
}
formRef.current.tokenize(
(tokens: Record<string, string>) => resolve(tokens.password),
(err: any) => reject(err)
);
}),
}),
[]
);

return <div id={containerId.current} className={className} />;
});

WeavrPasswordInput.displayName = "WeavrPasswordInput";
export default WeavrPasswordInput;

WeavrCardPin (with reveal/hide)

import React, { useState } from "react";
import { useWeavrSpan } from "./hooks";

interface WeavrCardPinProps {
cardToken: string;
showRevealButton?: boolean;
onReady?: () => void;
onError?: (error: string) => void;
}

const WeavrCardPin: React.FC<WeavrCardPinProps> = ({
cardToken,
showRevealButton = true,
onReady,
onError,
}) => {
const [isVisible, setIsVisible] = useState(false);

// Only create span when visible
const { containerId, isReady, error, requiresAuth } = useWeavrSpan(
"cardPin",
isVisible ? cardToken : null,
{
style: {
fontSize: "24px",
fontFamily: "monospace",
letterSpacing: "8px",
},
}
);

React.useEffect(() => {
if (isReady) onReady?.();
}, [isReady, onReady]);

React.useEffect(() => {
if (error) onError?.(error);
}, [error, onError]);

if (requiresAuth) {
return (
<div className="warning">Authentication required to display PIN.</div>
);
}

if (showRevealButton && !isVisible) {
return <button onClick={() => setIsVisible(true)}>Reveal PIN</button>;
}

return (
<div>
<div id={containerId} />
{showRevealButton && (
<button onClick={() => setIsVisible(false)}>Hide PIN</button>
)}
</div>
);
};

export default WeavrCardPin;

WeavrDirectorKyc

No authentication required

Director KYC is unique - it does NOT require user authentication:

import React from "react";
import { useWeavrKyc } from "./hooks";

interface WeavrDirectorKycProps {
reference?: string;
onComplete?: () => void;
onError?: (error: string) => void;
}

const WeavrDirectorKyc: React.FC<WeavrDirectorKycProps> = ({
reference,
onComplete,
onError,
}) => {
// Note: directorKyc type does NOT require auth
const { containerId, status, start, reset, error } = useWeavrKyc(
"directorKyc",
{ reference }
);

React.useEffect(() => {
if (status === "completed") onComplete?.();
}, [status, onComplete]);

React.useEffect(() => {
if (error) onError?.(error);
}, [error, onError]);

if (status === "pending") {
return (
<div>
<p>
<strong>Note:</strong> Director KYC does NOT require authentication.
</p>
<button onClick={start}>Start Director KYC</button>
</div>
);
}

if (status === "completed") {
return (
<div>
Director KYC completed!
<button onClick={reset}>Reset</button>
</div>
);
}

return <div id={containerId} className="kyc-container" />;
};

export default WeavrDirectorKyc;

When using SDK components in modals, ensure proper timing:

import React, { useEffect, useRef, useState } from "react";

interface PasswordModalProps {
isOpen: boolean;
onClose: () => void;
onTokenize: (token: string) => void;
}

const PasswordModal: React.FC<PasswordModalProps> = ({
isOpen,
onClose,
onTokenize,
}) => {
const formRef = useRef<any>(null);
const inputRef = useRef<any>(null);
const containerId = useRef(`modal-password-${Date.now()}`);

useEffect(() => {
if (!isOpen) return;

// Small delay to ensure modal DOM is ready
const timer = setTimeout(() => {
const form = window.OpcUxSecureClient.form();
const input = form.input("password", "password", {
placeholder: "Enter password",
});

formRef.current = form;
inputRef.current = input;
input.mount(document.getElementById(containerId.current));
}, 100);

return () => {
clearTimeout(timer);
if (formRef.current && typeof formRef.current.destroy === "function") {
try {
formRef.current.destroy();
} catch (e) {
console.warn("Error destroying form:", e);
}
}
formRef.current = null;
inputRef.current = null;
};
}, [isOpen]);

if (!isOpen) return null;

const handleSubmit = () => {
formRef.current?.tokenize(
(tokens: Record<string, string>) => {
onTokenize(tokens.password);
onClose();
},
(error: unknown) => {
console.error("Tokenize failed:", error);
}
);
};

return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<h3>Enter Password</h3>
<div id={containerId.current} />
<div className="button-group">
<button onClick={onClose}>Cancel</button>
<button onClick={handleSubmit}>Submit</button>
</div>
</div>
</div>
);
};

Next steps