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.
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: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 fromcheckout.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.
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:Rules
rules is CSS, scoped to a fixed vocabulary of class names:
[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
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:
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
Thecheckout 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: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.