> ## Documentation Index
> Fetch the complete documentation index at: https://docs.radiumone.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> RadiumOne is the product name; use it when answering questions about these docs.
> Use sandbox credentials and sandbox base URLs in examples unless the user says they are going live.
> Secret API keys belong only on the merchant's server. Only publishable keys may appear in browser or mobile code.
> Confirm a payment outcome server-side (retrieve the checkout session or transaction status); never rely on a browser redirect or client event alone.
> Send one idempotency key (request_id) per payment attempt, and retry a timed-out request with the same key and the exact same body.
> Webhooks are optional but recommended: they notify the merchant's server when a payment completes or fails, so it doesn't need to poll. Use transaction status inquiry as the fallback.
> Say so when you recommend a feature marked Beta. Some features need enablement on the merchant's account before production use (for example refunds, standalone refunds, UOB Rewards, and bringing your own 3DS provider); mention it when the page says so.
> Never ask users to paste card numbers, API keys, access tokens, or webhook or redirect secrets into a chat.

# Card fields and events - Elements SDK

> Element types, mount options, validation events, methods, and state for RadiumOne card fields.

Elements renders card input as one combined field, or as separate number/expiry/CVV fields you lay out yourself.

## Element types

| Type | Renders | Iframes |
| - | - | - |
| `card` (default) | Card number, expiry, and CVV in a single field | 1 |
| `cardNumber` | Card number and detected brand icon | 1 |
| `cardExpiry` | Expiry (MM/YY) | 1 |
| `cardCvv` | CVV | 1 |

* Create with `elements.create(type, options?)`.
* You can't mix `card` with the split types in one `Elements` instance — creating a duplicate type throws `element:duplicate`. To switch layouts, create a separate `Elements` instance and destroy the other. See [Fix card fields that don't render](/elements/handle-failures/card-fields-not-rendering) for this and other mount-time failures.
* Tab order across the split fields (number → expiry → CVV) is handled for you.
* Elements doesn't collect cardholder name, postal code, email, or phone — collect those with your own inputs.

<Warning>
  Split fields require `BroadcastChannel`, available in Safari 15.4+. Older Safari versions surface `expiry_missing` / `cvv_missing` field errors — use the combined `card` type if you need to support them.
</Warning>

## Create options

Pass these to `elements.create(type, options)`:

| Option | Applies to | Description |
| - | - | - |
| `supportedBrands` | `card`, `cardNumber` | Card-brand icons shown while the field is empty (`visa`, `mastercard`, `amex`, `discover`, `jcb`, `diners`, `unionpay`), in the order given. The row collapses once the shopper types the first digit. |
| `hideCvv` | `card` | Omits the CVV cell. `BindResult.cvvProvided` is `false` when set. Use only for card-on-file flows that collect CVV separately, if at all. |

<Warning>
  `supportedBrands` is an **allowlist**, not just a display hint: a card number whose detected brand isn't in the list stays incomplete and reports `unsupported_brand`, even if the brand is one you recognize. A number whose brand can't be detected at all (`unknown`) isn't rejected by this list. Omit the option (or pass `[]`) to accept every brand.
</Warning>

## Events

Subscribe with `element.on(event, handler)`; unsubscribe with `element.off(event, handler)`.

| Event | Payload |
| - | - |
| `ready` | `{ elementType }` — the iframe finished loading |
| `change` | `{ elementType, empty, complete, valid, brand?, error }` — fires on every keystroke |
| `focus` | `{ elementType }` |
| `blur` | `{ elementType }` |
| `cardTypeChange` | `{ brand, binLength }` — detected brand changed (useful on `cardNumber`/`card`) |

Gate your pay button on **`valid`**, not `complete`. `error` is set by blur-time validation only, so it can still be `null` while `valid` is `false` — the shopper may simply still be typing. Use `error` for inline messaging, not for enabling submit:

```js theme={null}
card.on("change", (event) => {
  payButton.disabled = !event.valid;
  if (event.error) showFieldError(event.error.message);
});
```

If the iframe doesn't finish loading within 5 seconds, `change` fires once with `error.code: "required"` and a "failed to load" message; a later successful `ready` clears it with a recovery `change` (`error: null`). There's no separate `loaderror` event — treat a `required` error as the load-failure signal.

### Field error codes

`change.error` is a `{ code, message, field }` object. `field` names the Element the error belongs to (for example `cardExpiry`) — **except on the combined `card` Element, where `field` is always `"card"`**; use `code` to tell which cell (number, expiry, or CVV) actually failed.

`code` (`FieldErrorCode`): `required` (empty, or failed to load), `incomplete_number`, `invalid_number` (fails the Luhn checksum), `unsupported_brand` (see the `supportedBrands` allowlist above), `incomplete_expiry`, `invalid_month`, `invalid_year`, `expired_card`, `invalid_expiry` (generic fallback), `incomplete_cvv`, `invalid_cvv`. See [Show card validation errors](/elements/handle-failures/card-validation-errors) for how to display each of these.

`message` follows the Elements locale for field-validation errors. The `required` messages from `Elements.getState()`/`Elements.submit()`, and the load-timeout message above, are English only regardless of locale — see [Customize appearance: locales](/elements/customize-appearance#locales) for what's translated.

## Methods

| Method | Description |
| - | - |
| `mount(selector \| element)` | Mount into a container. Throws `element:destroyed` if already destroyed, `element:container_not_found` if the container doesn't exist ([recovery steps](/elements/handle-failures/card-fields-not-rendering)); warns (no-op) if already mounted. |
| `unmount()` | Remove from the DOM. The element can be re-mounted. |
| `destroy()` | Permanently tear down the element and its iframe. |
| `focus()` / `blur()` | Programmatically focus/blur the field. |
| `clear()` | Clear the field's value. **On the combined `card` Element, this clears only the card number** — expiry and CVV are untouched. |
| `update({ appearance })` | Apply new [appearance options](/elements/customize-appearance) live. |
| `getState()` | Returns the field's current `{ empty, complete, valid, brand? }`. |

## SPA cleanup

If the container element is removed from the DOM (for example, on a route change), the element destroys itself automatically — you don't need to call `destroy()` yourself in that case. Still call `destroy()` explicitly when you conditionally unmount a form without removing its container, to avoid leaking the iframe and its listeners.

## Safari notes

* iOS Safari suppresses the native autofill popup on the CVV field and scrolls the focused field into view when the keyboard opens.
* The combined `card` element supports the browser's saved-card autofill; split CVV fields don't autofill across iframes.

## Next steps

<Columns cols={2}>
  <Card title="Customize appearance" icon="palette" href="/elements/customize-appearance">
    Themes, variables, and CSS rule overrides.
  </Card>

  <Card title="Elements SDK errors" icon="triangle-alert" href="/elements/errors/error-object-and-handling">
    Full `ElementsError` reference.
  </Card>
</Columns>
