# Migrate from Stripe Elements

MONEI Components mirror the Stripe Elements integration model: hosted iframes collect card data on your page, your PCI DSS scope stays at SAQ A, and your JavaScript never sees a card number. This page maps each Stripe concept to its MONEI equivalent.

## Concept mapping[​](#concept-mapping "Direct link to Concept mapping")

| Stripe                                    | MONEI                                                                                                                      |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `Stripe(publishableKey)`                  | No global constructor — Components take `paymentId` or `accountId` directly.                                               |
| `stripe.elements()`                       | [`monei.CardGroup({...})`](https://docs.monei.com/monei-js/reference/.md#cardgroup)                                        |
| `elements.create('card')`                 | [`monei.CardInput({...})`](https://docs.monei.com/monei-js/reference/.md#cardinput-component)                              |
| `elements.create('cardNumber')`           | [`monei.CardNumber({group})`](https://docs.monei.com/monei-js/reference/.md#card-parts)                                    |
| `elements.create('cardExpiry')`           | [`monei.CardExpiry({group})`](https://docs.monei.com/monei-js/reference/.md#card-parts)                                    |
| `elements.create('cardCvc')`              | [`monei.CardCvc({group})`](https://docs.monei.com/monei-js/reference/.md#card-parts)                                       |
| `element.mount('#id')`                    | `component.render('#id')`                                                                                                  |
| `stripe.createToken(cardElement)`         | `cardGroup.submit()` / `cardInput.submit()`                                                                                |
| `stripe.confirmCardPayment(clientSecret)` | [`monei.confirmPayment({paymentId, paymentToken})`](https://docs.monei.com/monei-js/reference/.md#confirmpayment-function) |
| `element.on('change', handler)`           | `onChange` prop                                                                                                            |
| `element.focus()` / `blur()` / `clear()`  | Same methods on a mounted part                                                                                             |
| `StripeElement--focus` etc.               | [`monei-component--focus` etc.](https://docs.monei.com/monei-js/reference/.md#card-part-state-classes)                     |
| `style` option (per element)              | [`style` object](https://docs.monei.com/monei-js/reference/.md#cardinput-style-object)                                     |
| `fonts` option                            | [`fonts: FontConfig[]`](https://docs.monei.com/monei-js/reference/.md#custom-fonts)                                        |

Key structural difference: MONEI has no element factory. The group travels as a plain `group` prop on each part — every Component is created with `monei.X({...})` and mounted with `.render()`.

## Split card fields[​](#split-card-fields "Direct link to Split card fields")

Stripe:

```
const elements = stripe.elements();

elements.create('cardNumber').mount('#number');

elements.create('cardExpiry').mount('#expiry');

elements.create('cardCvc').mount('#cvc');

const {token} = await stripe.createToken(elements.getElement('cardNumber'));
```

MONEI:

```
const cardGroup = monei.CardGroup({paymentId});

monei.CardNumber({group: cardGroup}).render('#number');

monei.CardExpiry({group: cardGroup}).render('#expiry');

monei.CardCvc({group: cardGroup}).render('#cvc');

const {token, error} = await cardGroup.submit();
```

Differences that work in your favor:

* **Aggregate `onChange` on the group** — `CardGroup({onChange: ({complete}) => ...})` tells you when all fields are valid. With Stripe you track each element's `complete` yourself.
* **Automatic focus advance** — number → expiry → CVC as the customer types, with `Backspace` walking back. Stripe's split elements do not advance focus.
* **Submit-time highlighting** — a failed `submit()` marks the offending part invalid; you only render the returned `error` string.

## Events[​](#events "Direct link to Events")

Stripe's `change` event payload maps almost one-to-one:

| Stripe `event`        | MONEI [`CardPartChangeEvent`](https://docs.monei.com/monei-js/reference/.md#cardpartchangeevent) |
| --------------------- | ------------------------------------------------------------------------------------------------ |
| `event.empty`         | `event.empty`                                                                                    |
| `event.complete`      | `event.complete`                                                                                 |
| `event.error.message` | `event.error` (already a display-ready string)                                                   |
| `event.brand`         | `event.brand` (`CardNumber` only)                                                                |

`ready` → use `onLoad`. `focus`/`blur` → `onFocus`/`onBlur` (no payload).

## Framework packages[​](#framework-packages "Direct link to Framework packages")

| Stripe                                                            | MONEI                                                                                                |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `@stripe/react-stripe-js` `<Elements>` provider + `useElements()` | [`useCardGroup()`](https://docs.monei.com/monei-js/react/.md#split-card-fields) — no provider needed |
| `<CardNumberElement />` etc.                                      | `<CardNumber group={group} />` etc.                                                                  |

Vue, Angular and Svelte packages follow the same shape — see [Vue](https://docs.monei.com/monei-js/vue/.md#split-card-fields), [Angular](https://docs.monei.com/monei-js/angular/.md#split-card-fields) and [Svelte](https://docs.monei.com/monei-js/svelte/.md#split-card-fields).

## Server side[​](#server-side "Direct link to Server side")

Replace PaymentIntents with MONEI payments: [create a payment](https://docs.monei.com/apis/rest/payments-create/.md) on your server, pass its `id` as `paymentId` to the group, then `submit()` + [`confirmPayment`](https://docs.monei.com/monei-js/reference/.md#confirmpayment-function) on the client — or confirm server-side with the token. Test with [MONEI test cards](https://docs.monei.com/testing/.md).
