Skip to main content

Element Events

Elements emit events that let your application respond to user interactions and state changes. All event data is accessed via event.detail.

Quick reference

EventFired whenPayload extras
readyElement iframe is loaded and interactive
changeInput value changesisValid, isEmpty, error
focusElement receives focus
blurElement loses focus
copyA copyButton is clickedsuccess, error?
errorAn error occurscode, message, status?, validationErrors?
networkChangedSelected network changes on a co-badged card (cardNumber and card only)network, previousNetwork, availableNetworks, selectionMode

Subscribing and unsubscribing

element.on() returns an unsubscribe function. Call it to remove the listener.

// Subscribe
const unsubscribe = cardNumberEl.on('change', (event) => {
const { isValid, isEmpty, cardBrand } = event.detail;
console.log({ isValid, isEmpty, cardBrand });
});

// Unsubscribe when no longer needed
unsubscribe();

Event payloads

All events include the following base fields in event.detail:

FieldTypeDescription
elementTypestringThe type of element that fired the event ('cardNumber', 'cvv', 'expiry', 'text', 'copyButton')
elementIdstringUnique element ID (useful when handling multiple elements)
timestampnumberUnix timestamp (milliseconds) when the event fired

ready

Fired when the element iframe has loaded and the user can interact with it.

// event.detail shape
{
elementType: string;
elementId: string;
timestamp: number;
}

change

Fired whenever the input value changes.

// event.detail shape (all elements)
{
isValid: boolean; // true when the input passes all validation rules
isEmpty: boolean; // true when no characters have been entered
error: ValidationError | null; // null when valid
elementType: string;
elementId: string;
timestamp: number;
}

cardNumber element adds the following fields:

{
cardBrand?: string; // detected brand (e.g. 'Visa', 'Mastercard')
last4?: string | null; // last 4 digits (PCI-safe, for display)
bin?: string | null; // BIN (first 6–8 digits)
cvvLengths?: number[] | null; // expected CVV lengths for the detected brand
potentialBrands?: string[]; // all brands that could match the current input
matchStrength?: number; // confidence score (0–1)
networks?: string[]; // networks a co-badged card supports (requires BIN lookup or coBadge)
selectedNetwork?: string; // currently selected network on a co-badged card
}

networks and selectedNetwork are populated only when binLookup or coBadge is enabled and the card participates in more than one network.

focus

Fired when the element receives keyboard focus.

// event.detail shape
{
elementType: string;
elementId: string;
timestamp: number;
}

blur

Fired when the element loses focus.

// event.detail shape
{
elementType: string;
elementId: string;
timestamp: number;
}

copy

Fired when a copyButton element is clicked. Only emitted by the copyButton element type. The payload reports whether the copy succeeded; it never contains the copied value.

// event.detail shape
{
success: boolean; // true if the value was copied to the clipboard
error?: string; // failure reason (no sensitive data) when success is false
elementType: string; // 'copyButton'
elementId: string;
timestamp: number;
}

In React, subscribe with the onCopy prop on CopyButtonElement:

<CopyButtonElement
allowClipboard
onCopy={(event) => {
if (event.detail.success) {
showToast('Copied to clipboard');
} else {
showToast(`Copy failed: ${event.detail.error}`);
}
}}
/>

error

Fired when an error occurs during element operations (mount failure, API error, etc.).

// event.detail shape
{
code: ErrorCode; // machine-readable code
message: string; // human-readable description
status?: number; // HTTP status (for API errors)
validationErrors?: Record<string, string[]>; // field-level errors from API
elementType: string;
elementId: string;
timestamp: number;
}

networkChanged

Fired when the selected payment network changes on a co-badged card — through automatic selection, the built-in selector, or a call to setNetwork(). Only cardNumber and card elements emit this event, and only when binLookup or coBadge is enabled.

// event.detail shape
{
network: string; // the newly selected network
previousNetwork?: string; // the network selected before this change
availableNetworks: string[]; // all networks the card supports
selectionMode: 'auto' | 'user'; // how the selection was made
elementType: string;
elementId: string;
timestamp: number;
}
cardNumberEl.on('networkChanged', (event) => {
const { network, availableNetworks } = event.detail;
console.log('Network is now', network, 'of', availableNetworks);
});

See Co-Badge Support for the full co-badge workflow, including building your own network picker with setNetwork().


ValidationError type

When event.detail.error is not null on a change event, it has this shape:

interface ValidationError {
code: string; // machine-readable error code
message: string; // human-readable error message
}

ErrorCode values

The following values are possible for event.detail.code on the error event:

CodeCategoryDescription
MOUNT_ERRORInfrastructureFailed to mount the element iframe
POSTMESSAGE_TIMEOUTInfrastructurePostMessage did not receive a response within timeoutMs
IFRAME_LOAD_ERRORInfrastructureElement iframe failed to load
INITIALIZATION_ERRORInfrastructureSDK failed to initialize
API_ERRORAPIBasis Theory API returned an error response
NETWORK_ERRORAPINetwork request failed (no response received)
VALIDATION_ERRORClientInput failed validation before an API request was made
INVALID_CONFIGURATIONClientInvalid SDK or element options were provided
UNKNOWN_ERRORClientAn unexpected error occurred

Examples

Track form validity across multiple elements

const formState = {
cardNumber: { isValid: false },
expiry: { isValid: false },
cvv: { isValid: false },
};

function updateSubmitButton() {
const allValid = Object.values(formState).every((s) => s.isValid);
document.getElementById('submit').disabled = !allValid;
}

cardNumberEl.on('change', (event) => {
formState.cardNumber.isValid = event.detail.isValid;
if (event.detail.cardBrand) {
document.getElementById('card-brand').textContent = event.detail.cardBrand;
}
updateSubmitButton();
});

expiryEl.on('change', (event) => {
formState.expiry.isValid = event.detail.isValid;
updateSubmitButton();
});

cvvEl.on('change', (event) => {
formState.cvv.isValid = event.detail.isValid;
updateSubmitButton();
});

Handle errors

cardNumberEl.on('error', (event) => {
const { code, message, status } = event.detail;

switch (code) {
case 'MOUNT_ERROR':
case 'IFRAME_LOAD_ERROR':
showError('Could not load the payment form. Please refresh and try again.');
break;
case 'POSTMESSAGE_TIMEOUT':
showError('Request timed out. Check your network connection.');
break;
case 'API_ERROR':
if (status === 400) {
showError('Invalid card details. Please check and try again.');
} else {
showError(`API error ${status}: ${message}`);
}
break;
default:
showError(message);
}
});

Show loading state until all elements are ready

const ready = { cardNumber: false, expiry: false, cvv: false };

function checkAllReady() {
if (Object.values(ready).every(Boolean)) {
document.getElementById('loading').style.display = 'none';
document.getElementById('form').style.display = 'block';
}
}

cardNumberEl.on('ready', () => { ready.cardNumber = true; checkAllReady(); });
expiryEl.on('ready', () => { ready.expiry = true; checkAllReady(); });
cvvEl.on('ready', () => { ready.cvv = true; checkAllReady(); });