Skip to main content
Elements is in public beta. It works and it takes real payments, but the API can still change and a few things are missing (see Known limits at the bottom). If you build on it now, pin a copy of your integration code somewhere you can find it again, and tell us what breaks.

What this is

The Embed SDK opens our checkout in a modal on top of your page. Elements is the other option: you get the checkout as separate pieces - the cart summary, the email field, the payment method list, the pay button - and you decide where each one goes and what it looks like. The obvious question is why you’d want that when the modal already works. Two reasons, and if neither applies to you, use the Embed SDK, it’s less work:
  • The checkout is part of your page, not a layer over it. It sits in your layout, in your column, next to your product photography.
  • It wears your brand. Your colours, your typeface, your corner radius, your spacing.
The trade-off is that you’re now responsible for the layout, and there’s more to get wrong.

Live demo

A pretend shop with the all-in-one element in its sidebar, wearing that shop’s brand

Quick start

One script tag, no build step, no package to install:
Then a div to mount into, and four lines:
That’s a complete, working checkout: cart, email, payment methods, terms, and a pay button. It’s the checkout element - everything in one piece. Most integrations should start here and only split it up if they actually need to.

The elements

elements.create(type) takes one of these: Every element you create from the same elements(...) call shares one cart. Change the quantity in summary and the total in checkout updates on its own - you don’t wire that up.

How it works

Worth two minutes, because it explains most of the rules further down. Each element is an iframe served from checkout.komerza.com. Card numbers, the buyer’s email and the order itself never touch your page or your JavaScript - which is the point, and it’s what keeps your PCI scope where it is. Alongside your elements we mount one hidden frame, the controller. It owns the cart, talks to our backend, and is the only thing allowed to create an order. Your elements don’t talk to each other directly; they send changes to the controller and it tells everyone what the new state is. That’s why two elements can never disagree about the total. Two consequences you’ll actually notice:
  • Your JavaScript can’t reach inside an element. Cross-origin, deliberately. Everything you need comes through elements.on(...) and the methods below.
  • Elements paint no background of their own, in any theme. They inherit whatever’s behind them, so a checkout in a white card looks like part of that card.

Styling

This is the part Elements exists for.

Tokens

tokens are the broad strokes. Set an accent colour and we regenerate the whole primary ramp from it, so it reaches everything that reads that ramp rather than just the pay button.
Set mode explicitly if your page is always light or always dark. The default follows the buyer’s OS setting, so a permanently light page viewed by someone in dark mode gets a dark checkout on a white panel.

Fonts

An element is a separate document from your page, so a font your page loads is not available to it - the checkout will quietly fall back to a system face. Load fonts into the elements explicitly:
Google Fonts only, and we rebuild the URL from the parts rather than using what you pass. Any other stylesheet would be applied to a payment page without passing through the rules sanitizer below, which would defeat the whole thing.

Rules

rules is CSS, scoped to a fixed vocabulary of class names:
Selectors must be a single class from the list below, optionally narrowed by one state attribute ([data-selected], [data-disabled], [data-loading], [data-invalid], [data-locked]) and one pseudo-class (:hover, :focus, :focus-visible, :active, :disabled, :checked). No descendant selectors, no element names, no ids. That’s narrower than real CSS on purpose. A checkout that can be restyled arbitrarily can be made to lie about what it charges - hide the total, cover the pay button, dress a decline up as a success. So a handful of properties are refused (position, content, z-index, pointer-events, transform, clip-path, and anything that fetches a URL), and !important is stripped. Everything cosmetic is yours. Anything we refuse is reported, never dropped silently:

The class names

These are a promise. Once a class is on this list it keeps existing and keeps meaning the same thing, even as we rewrite what’s behind it.

Starting from nothing

If you’d rather build the whole appearance yourself, preset: "bare" strips our cosmetics down to structure - no borders, no backgrounds, no radius, no padding:

Changing the brand later

Restyles in place. No remount, no flash.

Events

error is the one to handle. Everything that can fail arrives there in one shape, so you don’t have to watch each element separately:
Handle e.code where you can. It’s the part that tells you what to actually do - “out of stock” wants a different response from “that card was declined”.

Taking payment

The checkout element has its own pay button and drives the whole thing. Nothing else to do. If you’ve split the elements up and want your own button, call confirmPayment yourself:
returnUrl matters for the gateways that take over the whole window - without it there’s nowhere to send the buyer back to. Most gateways render inline and never leave your page, but some can’t.

Letting the buyer change their mind

Once an order exists the cart is locked, so a buyer who spots a typo isn’t stuck:

A complete example

Content Security Policy

If your site sends a CSP, elements need two directives:
Using nonces? Pass yours in and we’ll put it on every frame we create:

Known limits

Straight list of what isn’t there yet, so you don’t find out the hard way.
  • No npm package or React wrapper. Script tag only for now.
  • No currency selector element. Adaptive currency still works; there just isn’t a picker.
  • The API can change. It’s a beta. We’ll tell you before anything breaks, but treat it as provisional.

Which one should I use?

Use the Embed SDK

You want a buy button that opens checkout in a modal. It’s a script tag and a data attribute, and it’s done in five minutes.

Use Elements

You want checkout inside your page, laid out and styled as part of your design, and you’re willing to own the layout.
Last modified on August 29, 2026