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.
Recommended directory structure
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;
Modal integration
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
- Troubleshooting—Common issues and solutions