React Bindings
Providers, field components and hooks that mount the Wirex card frames and handle their lifecycle for you.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| PCI Compliant SDK | Isolation model and package map |
| SDK Setup | Client id, environments, installation, CSP |
| Collect a Top-Up Card | Tokenize parameters and results |
| Events and Error Handling | Failure modes shared with the vanilla packages |
Overview
@wirexapp/card-react is the first-party React binding: providers and field components for card
collection, and self-contained components for reveal and 3DS. It offers the same guarantees as the vanilla
packages — card data exists only inside Wirex-served iframes — with mounting and teardown handled for you.
npm install @wirexapp/card-reactreact >= 18 is a peer dependency. Every component is client-only; 'use client' is compiled in, so in
the Next.js App Router they work inside your client components with no extra directive.
For frameworks other than React, integrate the vanilla packages directly. The binding adds convenience, not
capability.
Top-Up Collection
import {
CardTopUpProvider,
CardNumberField,
CardExpiryField,
CardCvvField,
CardholderNameField,
useCardTopUp,
} from '@wirexapp/card-react';
const sdk = { environment: 'uat', clientId: 'a4f6c1e2-9b03-4d77-8a51-2f0c6b8e4d19' };
export function AddCardScreen() {
return (
<CardTopUpProvider
sdk={sdk}
auth={{ getBearerToken: () => session.getAccessToken() }}
appearance={{ theme: 'dark' }}
>
<CardNumberField />
<div className="row">
<CardExpiryField />
<CardCvvField />
</div>
<CardholderNameField />
<SubmitButton />
</CardTopUpProvider>
);
}
function SubmitButton() {
const { ready, state, tokenizeTopUp } = useCardTopUp();
const submit = async () => {
const { cardId } = await tokenizeTopUp({ billingDetails: getBillingAddress() });
// Continue with estimate, execute and 3DS.
};
return (
<button disabled={!ready || !state?.complete} onClick={submit}>
Continue
</button>
);
}Provider Props
| Prop | Type | Required | Description |
|---|---|---|---|
sdk | object | Yes | { environment?, clientId? } — the same config the vanilla creators take |
auth | object | Yes | { getBearerToken } — returns a fresh user token |
appearance | object | No | Whitelisted styling tokens — see Appearance and Localization |
strings | object | No | Translated labels and validation messages |
onChange | (state) => void | No | Aggregated validity and detected brand |
onReady | () => void | No | Fires once, when every field of the profile has mounted |
onMountError | (error) => void | No | A field iframe failed to load or initialize |
children | node | Yes | Field components and your own layout |
useCardTopUp()
useCardTopUp()| Value | Type | Description |
|---|---|---|
ready | boolean | true once all four fields have mounted |
state | object | null | Latest { complete, brand, fields } snapshot. null before the first change event |
tokenizeTopUp | function | Same parameters and result as the vanilla tokenizer |
Field Components
| Component | Field |
|---|---|
CardNumberField | number |
CardExpiryField | expiry |
CardCvvField | cvv |
CardholderNameField | name |
Each renders one iframe and accepts className and style for its container only. The field's own
appearance comes from the provider's appearance tokens, not from your CSS. Place them anywhere inside the
provider, in any layout.
Payout Collection
The same model with the card-number field only. Recipient name and labels are non-sensitive — collect them
with your own inputs.
import { OctDestinationProvider, CardNumberField, useOctDestination } from '@wirexapp/card-react';
<OctDestinationProvider sdk={sdk} auth={auth}>
<CardNumberField />
{/* your own inputs for cardholder name, label, third-party flag */}
<SendButton />
</OctDestinationProvider>;
// Inside SendButton:
const { ready, tokenizeOctDestination } = useOctDestination();
const { externalCardId, last4 } = await tokenizeOctDestination({
cardholderName,
cardLabel,
isSaved: true,
isThirdParty: true,
});OctDestinationProvider takes the same props as CardTopUpProvider. useOctDestination() returns
{ ready, state, tokenizeOctDestination }. Rendering CardExpiryField, CardCvvField or
CardholderNameField under it throws — those fields are not part of the oct-destination profile.
Reveal
import { CardReveal } from '@wirexapp/card-react';
<CardReveal
sdk={sdk}
cardId={card.id}
actionToken={actionToken}
fetchDetails
fetchCvv
fields={['cardholder', 'number', 'expiry', 'cvv']}
cardholderName=USER.DISPLAYNAME
auth={{
getBearerToken: () => session.getAccessToken(),
userEmail: user.email,
userWallet: user.walletAddress,
}}
onStateChange={({ status, errorMessage }) => status === 'error' && toast.error(errorMessage)}
/>;Props mirror the vanilla config plus sdk, className, style
and onMountError. The component remounts — and refetches — when cardId or actionToken change. A fresh
token is a fresh reveal.
3DS Challenge
import { ThreeDsChallenge } from '@wirexapp/card-react';
<ThreeDsChallenge
url={execute.three_ds_state.url}
onComplete={status => finishTopUp(status)}
onError={() => finishTopUp('Failed')}
style={{ height: 480, background: '#fff', borderRadius: 8 }}
/>;Props mirror the vanilla config plus className and style.
Give the container real dimensions — issuer pages assume 500×480 pixels or larger and a light background.
Render your own cancel control and unmount the component; treat a user-cancelled challenge as Pending,
not Failed.
Re-Render Rules
The collect group is recreated only when one of these changes:
| Input | Effect on the frames |
|---|---|
sdk.environment, sdk.clientId, sdk.frameOrigin, sdk.framePath | The group is destroyed and recreated. The user's input is lost |
Provider profile — swapping CardTopUpProvider for OctDestinationProvider | The group is destroyed and recreated |
auth, appearance, strings, onChange, onReady, onMountError identity | No remount. The latest value is used at the next call |
The comparison is on the values inside sdk, not on the object's identity, so an inline sdk={{ … }}
literal does not remount on every render. Passing a new appearance object does not re-theme a mounted
frame — appearance is read at mount.
Every component is safe under React 18 StrictMode. Fields mount, unmount and remount without leaking
iframes or message listeners.
Exports
| Export | Kind |
|---|---|
CardTopUpProvider, useCardTopUp | Top-up collect context and hook |
OctDestinationProvider, useOctDestination | Payout collect context and hook |
CardNumberField, CardExpiryField, CardCvvField, CardholderNameField | Field components |
CardReveal | Own-card reveal element |
ThreeDsChallenge | 3DS step-up element |
PciFrameRequestError | Error class, re-exported for instanceof checks |
All shared types — PciAppearance, PciCollectChangeState, PciRevealElementConfig, PciRevealAuth,
tokenize parameter and result types — are re-exported from this package, so a React integration needs no
direct dependency on the vanilla packages.
Updated 15 days ago

