Skip to main content

Render modes: embed vs elements

This is the one home for the render-mode model. Embedded Fields renders in one of two modes, chosen per session via the integrationMode field when you create the session. Every page that mentions a mode states its assumption in one sentence and links here for the full model; read this once and the rest of the docs make sense.

  • embed (the default) — a monolith: one combined card iframe renders the whole payment surface together (card number + expiry + CVC, plus cardholder name, billing address, the payment-method picker, a returning buyer's saved cards, and any eligible Apple Pay / Google Pay buttons). You place only email and save-for-future-use yourself.
  • elementscomposable: you mount the card, email, cardholder, address, and the other pieces as discrete elements you place yourself, where your account supports it. For the card you keep the single combined box or split it into the three discrete card-number / card-expiry / card-cvc secure fields.

The choice is per session — the same account can create one session in embed and the next in elements, with no account-level setting that locks you to one mode.

elements falls back to embed silently — it never errors

Discrete rendering applies only where your account supports it. When it's unavailable, an integrationMode: "elements" session silently renders as embed — separately-mounted cardholder / address simply collapse back into the card iframe. Read the integrationMode echoed on the created session (and the capability map from vora.sessions.retrieve()) to confirm which surface you actually got, then code both layouts defensively. See Custom checkout with Elements for the create-session call.

Most integrations don't need this page

Embedded Fields renders as one combined surface by default (integrationMode: "embed"), and the Element reference documents that default surface completely. Read on only if you create a session with integrationMode: "elements" to render the payment surface as discrete, separately-placed fields.


How the SDK reports it — element render modes

vora.sessions.retrieve(sessionId) returns a capability map. Each element renders in one of three modes for the active session:

  • NATIVE — the gateway ships a UI primitive for this element and the SDK wraps it (one iframe; the gateway owns internal layout; your style option translates to the gateway's theme API).
  • PROXY — the SDK renders the DOM itself, no iframe; your CSS has full control. Identical everywhere.
  • MONOLITH — the main embed iframe already contains this element's surface. There's no separate mountable slot — the element returns null at create time.

Your code never has to name or detect the underlying vendor — the capability map is the contract.


The two rendering modes (today)

ModeHow you set itCard-field shapeWhat you place yourself
embed (default)integrationMode: "embed" (or omit)One combined card box — number + expiry + CVC together, plus cardholder, billing address, wallets and the payment-method picker — all inside a single iframeOnly email and save-for-future-use are separate elements you place
elements (where supported)integrationMode: "elements"Either one combined card box (number + expiry + CVC together) or the three discrete secure fields card-number / card-expiry / card-cvc placed independentlyYou place card (or the three discrete card fields), email, cardholder, address, save-for-future-use and payment-method-picker as separate elements

Splitting the card into separate fields. In elements mode you can render the card as the combined box or as the three discrete secure fields — card-number, card-expiry, and card-cvc — each mounted into its own container (they share one secure-fields controller and tokenize together via one submit()). See Discrete card fields. On a binder without discrete-field support, a discrete field's .mount() throws frame_unsupported_element — use the combined card element instead. In embed mode the card is always the one combined box.


Capability matrix

Elementembed (default)elements
cardMONOLITH (one iframe contains the entire checkout)NATIVE (one combined number/expiry/CVC card iframe)
card-number / card-expiry / card-cvc(not applicable — card is one combined box)NATIVE (three discrete secure fields, mounted independently)
cardholderMONOLITH (inside the main card iframe)PROXY (SDK-rendered input)
emailPROXYPROXY
address (billing)MONOLITH (inside the main iframe)NATIVE (account-shipped address iframe)
payment-method-pickerMONOLITHPROXY (SDK-rendered button row)
save-for-future-usePROXY (consent surface is always SDK-rendered for compliance)PROXY
saved-methodsMONOLITHNATIVE / PROXY (not usable yet)

Wallet buttons (Apple Pay / Google Pay) render inside the card mount in every mode when the buyer's device and your domain are eligible — see Element reference → Apple Pay & Google Pay. There's no separate row element to position.

The per-element options (cardholder, address, picker, etc.) and their collect shapes live in the Element reference — this page is only the render-mode model.


What each mode means in practice

  • embed (default) — you mount card and that's effectively your whole payment surface. Its iframe contains the card fields, wallets, picker, and cardholder. email and save-for-future-use are still separate PROXY elements you can place and style freely; the other elements either return null or render inside the monolith. This is the path the Element reference documents.
  • elements (where supported) — request integrationMode: "elements" per session and the SDK renders elements discretely where your account supports it (otherwise the session silently falls back to the default embed — never an error). You place card, email, cardholder, address, save-for-future-use, and payment-method-picker as independently-positioned elements, with full outer-styling control on the SDK-rendered (PROXY) ones. (The discrete saved-methods picker is not usable yet — the element ships in the SDK but returns no cards, so a returning buyer's saved cards surface inside the embed monolith, not as a discrete element.) For the card you have a choice: the combined card box (number + expiry + CVC together) or the three discrete secure fields card-number / card-expiry / card-cvc, each mounted independently — see Discrete card fields. Wallet buttons render alongside the card inputs inside the card mount.

Composing multiple elements

With integrationMode: "elements", mount each element where you want it and submit the collection as a unit:

const elements = vora.elements.create({
theme: { font: { size: "16px" }, color: { text: "#1a1a1a" } },
});

const card = elements.create("card", {});
const address = elements.create("address", { mode: "billing" });
const email = elements.create("email", {});

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

// One submit() collects from every mounted element, drives tokenization,
// and returns a single flat SubmitResult — branch error -> token -> charged.
const result = await elements.submit();

The elements collection shares theme defaults across child elements. With the default integrationMode: "embed" the same code is valid — but address and cardholder render inside the card iframe rather than at their own mount points, so mounting them separately has no visible effect.

The submit result is one flat SubmitResult object regardless of how many elements you mount — every field optional, discriminated at runtime in the order errortokencharged. The canonical interface and the full three-outcome contract live in one place: Element reference → SubmitResult shape. Per-element options (cardholder, address, styling) live in the Element reference; the styling model — what the style option themes vs what you style on your own DOM — lives in Customize the look & feel.


What's next