Skip to main content
This page is the curated reference. The complete, generated type reference lives at /kit/api/elements and is regenerated from the published typings. For a walkthrough, start with Web components setup.
The elements are light DOM with no markup of their own: no shadow root, no wrapper elements, no class names. They bind data into the markup you write and reflect their state as attributes for your CSS. The exceptions: <qb-product-image> renders one <img>, <qb-cart-count> writes its number, <qb-seo> writes into <head>, <qb-checkout> renders the checkout’s <iframe>, and <qb-currency-select>, <qb-consent-banner> and <qb-consent-settings> render a default <select>, dialog or button only when you give them no markup of your own.
@quickbutik/kit/elements is browser only. Importing it in Node throws ReferenceError: HTMLElement is not defined. Import it from client code (an Astro <script>, a Vue onMounted, a Svelte onMount).

Setup functions

configure

Builds the page’s ambient shop: one client and one shared cart store that every element finds on its own. By default it also sets up cookie consent (a default banner appended to <body> unless the page has its own <qb-consent-banner>) and the shop’s own analytics gated on it. Takes every ShopkitConfig option (except that consent takes the richer shape below) plus:
string
The currency the page browses in until the shopper picks one: sent on every product, cart and checkout request. Leave it out to start in the shop’s own currency. Prices format with the currency each response states (product.currency, cart.currency); this value is only the formatting fallback for a platform older than those fields. See Currencies.
string
BCP 47, for number formatting. Defaults to the browser’s.
Cookie consent for the page, on by default. false turns it all off: no banner, no analytics, nothing appended to checkout URLs. A ConsentStore you already hold is used as is.
boolean | ElementsAnalyticsOptions
default:"true"
The merchant’s own GA4, GTM and Meta pixel from the admin, gated on consent: nothing from Google or Meta loads before the shopper agrees (unless loadBeforeConsent). false keeps the banner and loads no pixel.
Call it once at startup. A second call replaces the ambient shop for elements mounted afterwards, while elements already on the page keep the first one.

defineElements

Registers the tags. Explicit rather than an import side effect, so the entry stays tree-shakeable. Idempotent. prefix: "shop-" gives <shop-product> for a name collision; warnOnUnhandledActions: false silences the typo warning.

Other exports

Every element class is exported too: QbShopElement, QbProductElement, QbOptionsElement, QbOptionValuesElement, QbAddToCartElement, QbBuyNowElement, QbProductListElement, QbProductImageElement, QbCartElement, QbCartItemsElement, QbCartCountElement, QbCheckoutButtonElement, QbCheckoutElement, QbOrderConfirmationElement, QbSeoElement, QbCurrencySelectElement, QbConsentBannerElement, QbConsentSettingsElement, QbConsentGateElement.

Bindings

Put these on any element inside a component. Values are paths into the component’s scope (product.images.0.url), never expressions. A missing branch renders empty instead of throwing. Bindings written on a scope-owning element itself (qb-product, qb-product-list, qb-options, qb-option-values, qb-cart, qb-cart-items, qb-order-confirmation) are never applied; each binds only what is inside it.

Repeats

qb-product-list, qb-options, qb-option-values and qb-cart-items clone their <template> once per item.
  • The <template> must be a direct child of the repeating element. Nested in a <ul> or <div>, it is never found and the list renders nothing.
  • Rows are inserted after the template as siblings, so the repeating element is the list container: style it as the grid.
  • Rows are keyed and reused, so a quantity input keeps its caret while its line re-renders.

Actions

data-qb-action on any element, resolved by the nearest enclosing component that knows the verb. click and change both dispatch. Quantity for an add, in order: data-qb-quantity="3" on the clicked element, an input[data-qb-quantity-input] inside the wrapper, the wrapper’s quantity attribute, then 1. In a cart row, input[data-qb-quantity] (no suffix) is the bound quantity of the line and commits on change.

Events

Every element emits qb:-prefixed CustomEvents that bubble and compose, so one listener on document catches them all.

qb-shop

Optional. The ambient shop from configure() or the script tag covers a normal page. Use <qb-shop> for two shops on one page, one section against a different API, one section selling into a campaign storefront, or a client you built yourself. A <qb-shop> ancestor wins for its subtree. Changing storefront-id builds a new client and cart store. currency is the currency the subtree browses in by default; changing the attribute later switches that client with setCurrency() without rebuilding it or its cart. A <qb-shop> for the same shop as the page reuses the page’s analytics hub.

qb-product

Resolves one product and holds its variant selection. React twin: <ProductProvider>. slug walks the catalog; on a large catalog resolve the product server-side and assign el.product. The product is fetched in the client’s currency and reloads when it changes; a product assigned as el.product is re-read by its id. The currency attribute is only a formatting fallback for a product that does not state its own product.currency. With analytics on, it tracks view_item once per product.
PriceView
VariantView

qb-options

Repeats its <template> once per option group the product actually has, in display order. No attributes. A product with no options renders nothing.

qb-option-values

Nested in a qb-options row, repeats once per value of that group. Keep unavailable values clickable; dim them with [data-available="false"]. Clicking one keeps the new choice and clears what contradicts it.

qb-add-to-cart

Wraps your button inside a qb-product and keeps its disabled in step: not addable until the selection pins one variant, or while a mutation is in flight.

qb-buy-now

“Buy now”: adds the selected variant to the remembered cart and goes to the hosted checkout. Sits inside a qb-product. After it navigates it stays pending until the page is restored from the back/forward cache.

qb-product-list

Repeats a product card. With no filter attribute it lists the catalog in the merchant’s order; any of search, category-id, sort-by, min-price, max-price switches to search. Changing an attribute aborts the previous request. A qb-product-image inside a row is handed that row’s product. Setting cursor replaces the page; it does not append. The list re-runs its query when the client’s currency changes, and product.priceFormatted uses each product’s own product.currency. With search set and analytics on, it tracks search once per term.

qb-product-image

Renders one <img> with the CDN URL resolved, reused across updates so a variant swap does not flash. Finds its product from an enclosing qb-product or list row.

qb-cart

Scope for a cart view. Shares the page’s single cart store. Loaded on first mount and never created speculatively. Re-reads the same cart when the client’s currency changes; formatted figures use cart.currency.

qb-cart-items

Repeats the cart lines.

qb-cart-count

Writes the cart’s item count as its own text. No attributes. Reflects zero and state.

qb-checkout-button

Wraps your button. Starts the session on click and navigates with location.assign. Disabled while the cart is empty; guarded against a double click. no-redirect stops the navigation so you can use event.detail.url yourself. There is no language attribute; the checkout uses the shop’s language unless you start it yourself with checkout.start({ language }).

qb-checkout

The hosted checkout rendered inline in an iframe. It follows the cart, resumes the return leg of redirect payment methods from ?qb_checkout_session= and ?qb_checkout_shop=, and falls back to the redirect when a shop or page cannot embed. See Embedded checkout. The detail of each checkout-* event is the matching EmbeddedCheckout event payload. Attributes are read once, when the session is created.

qb-order-confirmation

Polls the order confirmation on the thank-you page. The session id comes from session-id, a ?session_id= query parameter, or the session the kit remembered when the checkout started (the normal case). timedOut is not a payment failure. A direct visit with nothing remembered lands in status="unknown"; render a neutral page for it. With analytics on, a completed order is reported as the purchase event once, deduplicated across reloads.

qb-currency-select

The currency switcher. Reads the shop’s currencies (shop.get(), so the key needs checkout:read) and switches the client with setCurrency() on a choice. The choice is remembered, and every qb-product, qb-product-list and cart element on the page reprices (the cart elements by re-reading the same cart). A switch made anywhere else is reflected too. React twin: useCurrency().
The added <select> is the one piece of markup it writes; no <select> is generated next to static set-currency buttons. Hide it for a single-currency shop with qb-currency-select[empty] { display: none; }. See Currencies. The cookie banner. Left empty it renders the kit’s default accessible, styled dialog; with your own markup inside, it binds your markup to the consent store. A page configured by configure(), <qb-shop> or the script tag gets a default banner appended to <body> automatically, unless it places its own.
Every decision writes the qb_consent cookie and dispatches qb:consent on document with the state as detail. A “Cookie settings” control for a footer: reopens the banner’s preferences. Left empty it renders a <button type="button"> labelled in lang; or wrap your own link or button. Hidden when the page turned consent off. Content that waits for consent: an embedded video, a chat widget.
The direct <template> child is the gated content and is cloned in only once category is granted; every other child is the placeholder shown until then.

qb-seo

Writes the title, description, canonical, OpenGraph, Twitter card and JSON-LD into <head> from the product it sits inside. Everything it writes carries data-qb-seo and is removed on update. Only for client-rendered pages; a server-rendered page should use buildSeo().

Errors

Errors are mostly silent by design. A failed product lookup sets state="error" on qb-product. A failed cart mutation is captured by the store: the add resolves null, no success event fires, the cart elements reflect state="error", and the message is in cartStore.getSnapshot().error. qb:error with { error, context } fires for startup failures (no shop, a bad key), a checkout start that fails and a confirmation that cannot run.