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
errorAn error occurscode, message, status?, validationErrors?

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')
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)
}

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;
}

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;
}

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 or coordinator 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(); });