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

# CardElement

> The drop-in card block: number, expiration, and security code pre-arranged as one field group. Reached through `payments.create("card")`; drive your pay button off `onChange`, then confirm with `payments.createConfirmationToken()`. Card numbers never touch your page or ours. Two arrangements via `layout`: 'stacked' (default) or 'compact' (one row).

{/* whop-elements-docs-channel: upcoming */}

<Info>**Upcoming** — generated from the latest merged element source; documents unreleased development. Use the channel picker at the top of the sidebar for the docs of a published release.</Info>

{/* whop-elements-since: unreleased */}*In development — not yet part of a stable release.*

Belongs to the [`Payments`](/elements/upcoming/payments/overview) group. Render `<CardElement />` inside it (React), or call `payments.create('card', { … })` on the handle (vanilla) — consumer props and `on<Event>` callbacks both go in the create options / JSX props.

<Note>**Exclusive** — when using `CardElement`, you can't mount `PaymentElement` or `CardFields` in the same Payments handle: these are alternatives — mount one at a time; destroy it to mount another.</Note>

## Preview

A live, interactive demo of this element with sample data:

<div data-whop-demo-shell style={{ position: "relative", minHeight: "320px", transition: "min-height 200ms ease" }}>
  <div data-whop-demo-skeleton style={{ position: "absolute", inset: "0", borderRadius: "12px", background: "rgba(140, 140, 140, 0.12)", pointerEvents: "none", transition: "opacity 200ms ease" }} />

  <iframe data-whop-demo src="https://js.whop.cloud/elements/amber/payments/card/en/demo/index.html" title="CardElement demo" height="320" allow="payment; publickey-credentials-get; clipboard-write" style={{ position: "relative", width: "100%", border: "0", borderRadius: "12px", background: "transparent", colorScheme: "normal" }} />
</div>

## Usage

<div data-whop-usage="payments/card">
  <CodeGroup>
    ```tsx React theme={null}
    import { WhopElements, Payments, CardElement } from "@whop/elements-react";
    import { loadWhop } from "@whop/elements";

    function Example() {
      return (
        <WhopElements elements={loadWhop()}>
          <Payments /* options */>
            <CardElement onChange={(payload) => console.log("change", payload)} />
          </Payments>
        </WhopElements>
      );
    }
    ```

    ```html Vanilla theme={null}
    <script src="https://js.whop.cloud/elements/amber/elements.js" data-whop-elements></script>
    <script type="module">
      const payments = window.WhopElements().payments.create({ /* options */ });
      payments.create('card', { onChange: (payload) => console.log("change", payload) }).mount('#payments-card');
    </script>
    ```
  </CodeGroup>
</div>

## Props

<ResponseField name="layout" type="&#x22;compact&#x22; | &#x22;stacked&#x22;">
  Field arrangement: 'stacked' (default) puts the number on top with expiration and security code below; 'compact' is the classic single row — the number flexes, expiration and security code sit fixed-width to the right, all inside one visual container. Defaults to `"stacked"`.
</ResponseField>

## Events

Pass a callback in the create options (or React prop) to receive these.

### `onChange`

The card fields changed — `complete: true` means all three are filled so the confirm can run; drive your pay button off it. `brand` is the detected card network.

**Signature:** `((payload: { complete: boolean; brand: string; }) => void)`

### `onLoaderStart`

Fired the moment the element's own loading skeleton has painted inside its frame — the earliest point a consumer-managed loading state can hand off without ever exposing a blank. Always precedes `onReady`; most consumers only need `onReady`.

**Signature:** `(() => void)`

### `onReady`

Fired once the element has booted and painted its first complete frame.

**Signature:** `(() => void)`

### `onError`

Fired when the element fails to load or crashes; the element shows its own error fallback. `message` is human-readable; framework refusals also carry `code` (e.g. `HOST_SOURCE_FAILED`, with `sourceKey` naming the failed hostState key) so hosts can switch on codes, never message text.

**Signature:** `((e: { message: string; code?: string | undefined; sourceKey?: string | undefined; }) => void)`

## Methods

Call these on the element handle — the return of `create` (vanilla) or the component `ref` (React).

### `mount`

Place the element on the page: appends its container to `target` (a CSS selector or an element) and starts loading. Nothing renders until this is called. React consumers never call it — the component mounts itself.

**Signature:** `(target: string | HTMLElement) => void`

### `destroy`

Remove the element from the page and release its frame and subscriptions. Safe to call more than once. React consumers never call it — unmounting the component does it.

**Signature:** `() => void`

### `update`

Change this element's consumer props after mount — it re-renders with the merged props. React consumers never call it — updating the JSX props does the same.

**Signature:** `(options: Partial<CardElementProps>) => void`

## Styling

Each part below is a stable class name — safe to depend on. Restyle a part by mapping its class to a **style declaration object** under `appearance.classes` (properties camelCase or kebab-case, values as strings with units — the same shape as React's `style` prop). The element renders in its own frame, so page stylesheets can't reach it: these declarations are sanitized against a safe-property allowlist and injected inside the frame for you.

| Class                         | Targets                                                                                           |
| ----------------------------- | ------------------------------------------------------------------------------------------------- |
| `.whop-Card`                  | The card element root                                                                             |
| `.whop-CardError`             | The error pane shown when the card configuration cannot load                                      |
| `.whop-CardField`             | One card field cell — number, expiry, or CVC                                                      |
| `.whop-CardFieldError`        | The Invalid / Incomplete message under a card field                                               |
| `.whop-CardFieldGroup`        | The grouped card fields — number on top, expiry and CVC below                                     |
| `.whop-CardFieldInput`        | The bordered input container around a PCI field                                                   |
| `.whop-CardFieldInputFocused` | Added to the input container while its field is focused                                           |
| `.whop-CardFieldInputInvalid` | Added to the input container while its field is invalid or incomplete                             |
| `.whop-CardFieldRow`          | The compact single-row card field group — number flexing, expiry and CVC fixed-width to the right |
| `.whop-CardSaveNotice`        | The save-for-future-purchases consent line                                                        |

```ts theme={null}
const payments = whop.payments.create({
  appearance: {
    classes: {
      'whop-Card': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardError': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardField': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardFieldError': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardFieldGroup': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardFieldInput': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardFieldInputFocused': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardFieldInputInvalid': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardFieldRow': { borderRadius: '8px', fontWeight: '600' },
      'whop-CardSaveNotice': { borderRadius: '8px', fontWeight: '600' }
    }
  }
});

// restyle live at any point — the same shape through update()
payments.update({
  appearance: { classes: { 'whop-Card': { fontWeight: '700' } } }
});
```

In React, pass the same object as the `appearance` prop on `<Payments>`; `appearance` also applies globally at `WhopElements({ appearance })`.
