Skip to main content
Use this page to style the hosted field and react to state changes without owning raw card input.

Appearance example

const card = elements.create("card", {
  theme: "default",
});

card.mount("#card-element");

Supported event pattern

let cardState = {
  valid: false,
  brand: null,
  errors: {},
};

card.on("ready", () => {
  messageEl.textContent = "";
});

card.on("change", (event) => {
  cardState = event;

  submitButton.disabled = !event.valid;
  brandEl.textContent = event.brand ? event.brand.toUpperCase() : "";
  messageEl.textContent = Object.values(event.errors ?? {})[0] ?? "";
});

card.on("error", (event) => {
  messageEl.textContent = event.message ?? "Unable to initialize the card field.";
});
Call card.off("change", handler) when your component unmounts if your framework keeps the same card instance alive across renders.

Event payloads

ready fires when the hosted iframe is loaded. It does not mean the card details are valid. change fires whenever card input changes:
{
  "valid": false,
  "brand": "visa",
  "errors": {
    "number": "Card number is invalid"
  }
}
When the card brand is unknown, brand is null. When the current state is valid, errors is empty and valid is true. error fires when the hosted field fails to load or receives a payment failure outside an active tokenization request:
{
  "message": "Unable to initialize the card field."
}

Invalid states

Use change.valid as the only submit gate. Do not infer validity from the card brand alone.
card.on("change", ({ valid, brand, errors }) => {
  submitButton.disabled = !valid;
  cardBrandEl.textContent = brand ?? "";

  const firstError = Object.values(errors ?? {})[0];
  errorEl.textContent = firstError ?? "";
});
The errors object is keyed by the hosted field that needs attention. The exact keys are controlled by the hosted card field, so render the messages rather than hardcoding every possible key. Fix: confirm the script version, environment, and publicKey. Event handlers should update UI state only; final payment state still comes from your backend and webhooks.