mount() is start() plus a frame: one POST /checkout/handoff, with an embed block added.
The full-page redirect is still the default and the fallback. Use the embedded checkout on a route of its own; keep
<qb-checkout-button> or redirectToCheckout() on the cart page. Apple Pay and Google Pay are not available inline yet; they run in the full-page checkout.Who may frame a checkout
The session decides.mount() records your page’s origin on the session it creates, and only a session that carries one is served a framable document. A hosted checkout URL you build yourself records nothing, so a hand-built iframe around a hosted checkout URL is refused, and always will be.
The recorded origin is a permission to frame, not a restriction on who frames: the document’s frame-ancestors names https, so the checkout still renders when your page is itself inside something else (a CMS preview, a site builder’s editor). Your origin decides which page the checkout exchanges messages with.
mount() records the origin every time, so a cart that start() opened earlier still embeds when mount() picks it up.
Redirect payment methods
Klarna, Swish, Vipps MobilePay, iDEAL, Trustly and full-page 3-D Secure take the whole window to the provider. A frame cannot do that on its own, so the kit does:<prefix>_checkout_store).
Your page owes this flow two things:
- Render the checkout on load, and tolerate both parameters.
<qb-checkout>and<Checkout>pick them up, resume that session instead of creating a new one, and strip both from the address bar once the resumed frame has answered. A resume that fails before then leaves them, so a reload resumes again. - Do not redirect that URL away. A router that drops unknown query parameters, or a marketing redirect, breaks the return leg. The order is still created, but the shopper sees nothing.
Routing the return leg yourself
readReturnedCheckout(url?) accepts a URL (for example request.url), so a server component can decide before rendering whether this is a return leg. storeId is null when the URL carried nothing usable; resume() then falls back to the id remembered with the session, then the cart’s.
It follows the cart
<qb-checkout> and <Checkout> watch the shared cart store. You write no code for this:
- Nothing is mounted for an empty cart. While the cart loads or is empty, no session is created.
<qb-checkout>emitsqb:checkout-emptyand<Checkout>callsonEmpty, once per empty phase. The frame goes in by itself when an item exists. - Emptying the cart takes the frame down, because the session names a cart that no longer exists. The next item starts a fresh checkout. A frame resuming a return leg, or showing a completed order, is left alone.
- Changing the cart updates the checkout in place. Add a line or change a quantity through the kit’s cart store and the checkout re-reads the cart: lines, totals, shipping and the amount to pay follow, without losing what the shopper typed. If you change the cart some other way, or drive
mount()by hand, callcheckout.cartUpdated()after the change is saved.
When it cannot embed
Four expected situations, none of them a thrown error. Each resolves to a handle withstate: "fallback", emits fallback / qb:checkout-fallback with the hosted checkout url, and (unless fallbackRedirect: false) sends the shopper to the full-page checkout:
On
resume() the fallback URL is built for that session, so the shopper lands on its confirmation, never on a new checkout.
The handle
Events
On the element these are
qb:checkout-ready, qb:checkout-step, qb:checkout-event, qb:checkout-complete, qb:checkout-error, qb:checkout-fallback and qb:checkout-empty, all bubbling; element.checkout is the handle. In React they are onReady, onStep, onEvent, onComplete, onError, onFallback and onEmpty.
Every navigation is performed for you: navigate and a GET redirect with location.assign, a POST redirect with a hidden target="_top" form, and complete with a jump to the confirmed order. That landing is <successUrl origin>/success/<orderNumber>?hash=…&t=…, the same as the redirect checkout, so one thank-you route serves both.
Options
Everything
start() takes (successUrl, backUrl, cancelUrl, language, theme, prefill, cartId) is taken too. Set theme when the page is dark: the frame inherits nothing from your CSS.
The element and component read their attributes and props once, when the session is created. Changing them later is ignored rather than discarding a checkout the shopper may be paying in.
Not in the embed yet
- Wallets. Apple Pay and Google Pay are off inside the frame; cards and redirect methods work.
- Merchant pixels inside the frame. GA4, GTM and Meta are not injected into the frame: in a third-party context they would pollute attribution. Every commerce event is forwarded to your page as an
eventinstead (qb:checkout-eventon<qb-checkout>,onEventon<Checkout>), and with the kit’s analytics on (the default) it is tracked there automatically, thepurchaseincluded, gated on the shopper’s consent. See Consent and analytics. - A hand-built iframe. Use
mount(),<qb-checkout>or<Checkout>.
Content Security Policy
The embedded checkout needsframe-src https://pay.quickbutik.com in addition to the kit’s usual connect-src https://commerce.quickbutik.com and img-src https://cdn.quickbutik.com.