Skip to main content
Generated from the typings of @quickbutik/kit@1.8.0 as published on npm. Do not edit this page by hand: run npm run generate in tools/kit-api-reference. For guided, example-led documentation see the API reference.

Classes

Analytics

The hub: one track() the storefront calls, fanned out to every destination the shopper’s consent allows. Events are remembered (bounded) rather than dropped when a destination is not ready for them: before the shopper decides, a view_item waits, and is delivered the moment marketing is granted — or forgotten the moment it is denied. The same memory covers destinations that arrive late, which is the normal case: destinationsFromShop() needs one request to learn the merchant’s ids, and the product page’s view_item has usually fired by then. Gating happens at dispatch time, so withdrawing consent stops a destination immediately even though its script stays on the page. Purchases are deduplicated across page loads through the platform’s own localStorage key, and carried to destinations with the platform’s own Meta eventID, so a thank-you page can be reloaded without counting the order twice — and the platform’s server-side event, when there is one, merges with the browser’s.

Constructors

Constructor
Parameters
Returns
Analytics

Properties

requireConsent
False when the hub runs ungated (requireConsent: false).

Accessors

consentState
Get Signature
The decision the hub gates on; null when it runs ungated. Required consent with no store to read it from is an undecided shopper, forever — never null, which every destination reads as “everything granted”.
Returns
ConsentState | null
destinations
Get Signature
The destinations the hub holds, in order.
Returns
readonly AnalyticsDestination[]

Methods

addDestination()
Add a destination after construction — what destinationsFromShop() feeds in once the shop’s ids are known. It receives the events the hub still remembers, consent permitting.
Parameters
Returns
void
allows()
Whether a category may be used right now, by the hub’s own rule.
Parameters
Returns
boolean
attachCart()
Turn the cart store’s mutations into add_to_cart / remove_from_cart events — the quantities that changed, not the whole basket — and learn the shop’s currency and numeric store id from it. Both are kept once seen: a cart that goes away (bought, expired) does not unlearn the shop. Returns the detach function.
Parameters
Returns
Returns
void
destroy()
stop(), plus forget the cart and the remembered events.
Returns
void
observeCart()
Report a second cart’s add_to_cart / remove_from_cart through this hub, without making it THE cart: the currency and store id are still learned from the one attachCart was given, and attaching does not replace it. For a nested shop (a campaign storefront inside the site’s provider) that shares the outer hub rather than loading the pixels twice. Returns the detach function.
Parameters
Returns
Returns
void
removeDestination()
Stop delivering to a destination. Its script, if it loaded one, stays on the page (nothing can unload it); it simply receives nothing more. Adding the same object back resumes it without a replay.
Parameters
Returns
void
start()
Begin delivering: load what consent allows, subscribe to changes. Idempotent, and stop() undoes it, so a React effect can call the pair on every mount/unmount.
Returns
void
stop()
Returns
void
track()
Record an event and deliver it wherever consent allows.
Parameters
Returns
void
trackPageView()
Parameters
Returns
void
trackPurchase()
Report an order once. Returns false when this browser already reported it (a reloaded thank-you page) — the event is then not sent again. storeId falls back to the hub’s (its option, else the attached cart’s); without any the key is still unique per order, just not shared with the platform’s own surfaces.
Parameters
Returns
boolean

AsyncResource

One in-flight fetch and its result, observable, with no framework attached. The non-React twin of react/use-async.ts, and the same non-promise: this is NOT a cache. shopkit ships no data-layer opinion — it exists so the elements layer can load a product list without the package growing a query library. A superseded run is aborted and its late response discarded, which is what a search-as-you-type box needs: run() called five times must land the fifth answer, not whichever server replied last.

Type Parameters

Constructors

Constructor
Parameters
Returns
AsyncResource<T>

Properties

getSnapshot()
Returns
AsyncSnapshot<T>
subscribe()
Parameters
Returns
Returns
void

Methods

abort()
Cancel the in-flight run, if any, without changing the last result.
Returns
void
hydrate()
Adopt a result fetched elsewhere — a server render passing its data in.
Parameters
Returns
void
run()
Start (or restart) the operation, cancelling whatever was in flight. Resolves to the result, or to null when the run failed or was superseded — errors are captured on the snapshot rather than thrown, so a caller in an event handler cannot produce an unhandled rejection.
Parameters
Returns
Promise<T | null>

CartResource

The shopper’s cart. Two layers live here. The plain methods (create, get, addItem, …) are a thin, stateless mapping of the Cart API. The current/ensure pair adds the one piece of state a storefront always ends up writing itself: remembering WHICH cart this visitor has, in a way that works the same in the browser, in a server component and in a server action.

Constructors

Constructor
Parameters
Returns
CartResource

Methods

add()
Add to the remembered cart, creating it on first use. Recovers once from a cart the server has since dropped: a stale id in a month-old cookie would otherwise 404 the very first “add to cart” click of a returning visitor.
Parameters
Returns
Promise<Cart>
addItem()
Add a product. Adding the same product+variant again increments the existing line rather than creating a second one, so callers do not have to look first.
Parameters
Returns
Promise<Cart>
clear()
Empty the basket: delete the REMEMBERED cart server-side and forget it locally, so the next add() starts a fresh one.
The call a “Töm varukorgen” button wants, without the id bookkeeping delete needs. Three cases, none of them an error:
  • No remembered cart — nothing to clear, so no request is made. A visitor who never added anything, or a second click, causes no write.
  • The cart is already gone (404/410 from the delete — expired, or bought in another tab) — the goal was an empty basket and the shopper has one, so the stale id is forgotten all the same. Surfacing that as an error would leave a button that can never succeed, because the id it keeps retrying is the one that no longer resolves.
  • Any other failure (a network error, a 5xx) throws, and the id is KEPT: the cart may well still exist server-side, and forgetting it would strand those items in a cart this visitor can no longer reach.
The checkout session id, if one is remembered, is left alone — a thank-you page still needs it to confirm an order already paid for. fallbackCartId is the cart to delete when NONE is remembered — a cart rendered from a server read that this browser’s storage does not hold. A remembered id always wins over it.
Parameters
Returns
Promise<void>
create()
Create a new empty cart. Does not remember it — see ensure. On a client bound to a campaign storefront (or when storefrontId is passed here) the cart is created BOUND to that campaign: the platform prices it with the campaign’s overrides on every read, and a checkout session created from it inherits the binding. Otherwise the request carries no body, as it always has. This is the stateless call, so any campaign may be named — pass the returned cart.id as cartId to checkout.start() to check it out.
Parameters
Returns
Promise<Cart>
current()
The remembered cart, WITHOUT creating one. Use this wherever a missing cart is a legitimate answer — a header badge, a server-rendered cart page — so a crawler or a first-time visitor does not cause a write. A remembered id that no longer resolves is forgotten and reported as null.
Parameters
Returns
Promise<Cart | null>
currentId()
The remembered cart id, or null when this visitor has none yet.
Returns
Promise<string | null>
delete()
Delete the cart server-side. Also forgets it locally.
Parameters
Returns
Promise<void>
ensure()
The remembered cart, creating and remembering one if needed. This is the call that needs writable storage. In a read-only context (an RSC, which cannot set cookies) the cart is still created server-side and returned — it just is not remembered, so prefer calling it from a place that CAN write (a server action, a route handler, the browser). A cart it has to create is bound to the client’s campaign storefront — see create. A cart it merely finds is returned as it is, which on a bound client is a cart this same client created, because a bound client remembers its cart under its own storage prefix. A per-call storefrontId may only restate the client’s binding: this call reuses the remembered cart, and a different campaign could neither apply to it nor be remembered safely (see StorefrontAttribution).
Parameters
Returns
Promise<Cart>
get()
Fetch a cart by id, or null when it no longer exists.
Parameters
Returns
Promise<Cart | null>
removeItem()
Parameters
Returns
Promise<Cart>
updateItem()
Set an absolute quantity on a cart LINE (item.id, not the product id).
Parameters
Returns
Promise<Cart>

CategoriesResource

The shop’s category tree. Read-only for a storefront credential.

Constructors

Constructor
Parameters
Returns
CategoriesResource

Methods

get()
A single category by prefixed (cat_12) or numeric id, or null. A bare number is prefixed on the way out — the gateway validates the path param against ^cat_\d+$ and 400s a plain 12, the same trap products.search({ categoryId }) already normalises away.
Parameters
Returns
Promise<Category | null>
list()
One page of categories. Pass root: true for the top level, or a parentId (prefixed cat_…) to walk one level down.
Parameters
Returns
Promise<Page<Category>>

CheckoutResource

Checkout, as a headless storefront uses it: build a session from a cart, hand the shopper to the hosted Quickbutik checkout, then confirm the order when they come back. Payment itself is deliberately NOT part of this surface. The hosted checkout owns the PSP integration, PCI scope, 3-D Secure, wallets and the payment retry logic — the storefront owns the catalog, the cart and the thank-you page.

Constructors

Constructor
Parameters
Returns
CheckoutResource

Methods

buyNow()
Add a product and hand off immediately — the “Buy now” button. Uses the remembered cart, so an existing basket is carried along rather than replaced. Returns the URL rather than navigating, exactly as start does; useCheckout().buyNow and <qb-buy-now> are the navigating wrappers. The result also carries cart — the cart as the add left it — so a caller that keeps its own cart state (the shared CartStore does) can fold it in without a second read: a page that stays put (no-redirect, a new tab) or is restored from the back/forward cache must not show a badge that missed the item it just added.
Parameters
Returns
Promise<BuyNowResult>
confirmation()
One snapshot of the order-creation status for a handoff. Order creation is asynchronous: the PSP authorizes, then the platform builds the order. Between those two moments the status is processing_payment and there is no order number yet. Takes either handle — a checkout-v2 session id or a legacy order uuid (both arrive as StartCheckoutResult.handoffId); the platform resolves which it is. Read the statuses with care on a legacy handoff: that checkout leaves a failed payment looking untouched, so no_attempt means “not paid yet”, NOT “the shopper never tried”. See LegacyHandoff.
Parameters
Returns
Promise<SessionConfirmation>
createSession()
Create a checkout session for a cart and remember it. successUrl may be any https URL — http only for localhost/loopback — see CreateSessionInput.successUrl. The same rule applies to cancelUrl and backUrl (where the checkout’s “continue shopping” links point). Only the ORIGIN of successUrl is used for the return redirect. A campaign storefront (storefrontId on the input, else the client’s storefront) is sent along with its surface; the platform then prices the session with the campaign, refuses with a typed error when the campaign is not live, a quantity limit is exceeded or the cart belongs to another campaign (see storefrontRefusal()), and puts the campaign on the order. With neither set, nothing is sent and a cart that was created bound to a campaign passes its own binding on server-side. Because the caller names the cart here, any campaign may be named.
Parameters
Returns
Promise<CheckoutSession>
currentSessionId()
The remembered checkout session id, or null.
Returns
Promise<string | null>
embedUrl()
The URL of the checkout document to FRAME for a session — /embed/{storeId}/{sessionId}. The sibling of hostedUrl, and everything that doc says about storeId applies here verbatim: it is the shop’s NUMERIC id (Cart.storeId), not the prefix in the publishable key, and both ids ride the path because storage is partitioned — or absent — inside a third-party frame, so the URL has to carry everything needed to rehydrate. A distinct top-level /embed/ prefix rather than a path under /checkout/: the two documents are not interchangeable. The embed one is chrome-less, never navigates itself, and is served with a frame-ancestors header naming the session’s recorded embed origin — so a session created WITHOUT an embed block renders a refusal here, whatever URL you build. mount is the supported way to reach this; build it by hand only when you are framing the checkout yourself.
Parameters
Returns
string
finalize()
Forget the cart and session ids. Called automatically once a confirmation comes back completed, so the next visit starts a clean basket instead of resurrecting a cart that has already been bought. The shop’s numeric store id remembered off the cart is kept, here as everywhere a cart is forgotten (a header badge finding the cart gone, cart.clear()): it is a constant of the shop, not of the order, and it is what a thank-you page keys the purchase event on (rememberedStoreId()). A reload of that page — with the session id on its URL — has to build the same key as the first visit, or the order is counted twice. The copy kept next to the checkout session goes with the session, as documented on SessionStore.
Returns
Promise<void>
getSession()
Read a session. Useful on the thank-you page to show what was bought (data.cart_products, data.order_total) without a second cart lookup. Null when the session is unknown or has been cleaned up.
Parameters
Returns
Promise<CheckoutSessionSnapshot | null>
hostedUrl()
The URL of the checkout-v2 hosted checkout for a session. /checkout/{storeId}/{sessionId} — the canonical, shareable, resumable form. Both ids are in the path because the checkout must be able to bootstrap itself from the URL alone (a link opened on another device has no storage to fall back on). checkout-v2 only, and it asks the platform nothing. A shop on the legacy checkout needs a URL naming an order the platform has to create first, which no synchronous local builder can produce — so for such a shop this returns a link that cannot be opened. Prefer start, which asks which checkout the shop runs and returns the right URL either way; reach for this only when you hold a checkout-v2 session id and want the URL without a round trip. storeId is the shop’s NUMERIC id, Cart.storeId. It is not the prefix in the publishable key (shopkit.shopPrefix, 101928Y), which the checkout answers with “We couldn’t find 101928Y”. The id is taken from the input when given, else from the one remembered off the last cart this client received; when neither exists this throws rather than guess — pass storeId: cart.storeId, or use start, which has the cart in hand.
Parameters
Returns
string
mount()
Render the checkout INSIDE this page, in an iframe served from the checkout’s own origin.
One request, not two: this is start’s call to /checkout/handoff with an embed block added, so a mounted checkout costs a storefront exactly what a redirected one does. It always resolves. When there is nothing to frame — the shop runs the legacy checkout, the platform returned no embed URL, or this page cannot navigate its own top window — the handle comes back in its fallback state, emits fallback, and (unless fallbackRedirect: false) sends the shopper to the hosted checkout instead. None of those is a bug in the calling page and none of them is retryable, so none of them throws. A genuine failure — no cart, a rejected key, a 500 — still rejects, exactly as start() would. The same hosted URL is kept for one more case, decided later: a frame that is built but never answers (this page is not the origin the session was created for, typically). Fifteen seconds in, the handle takes the frame down, moves to fallback with reason handshake-timeout, and sends the shopper the same way — so a misconfigured embedOrigin costs a redirect, not a sale. One checkout per container. Mounting into an element that already shows one destroys the earlier handle — its frame, its listeners, its handshake timer — before the new frame goes in, so a page that calls mount() again from a re-render or a “try again” button ends up with one checkout, not two stacked. Last mount wins. The platform hands the same cart the same session back, so the new frame shows the same checkout at the same step; nothing is charged twice. <qb-checkout> lives by the same rule when it is detached and put back. Keep the handle if you want to destroy() it yourself; you do not have to for a second mount(). Browser only: it builds a DOM node and listens for message.
Parameters
Returns
Promise<EmbeddedCheckout>
parseReturnUrl()
Parse the hosted checkout’s post-payment redirect. The shopper is sent to <successUrl origin>/success/<orderNumber>?hash=…&t=… — note that only the ORIGIN of successUrl is used, so this path is where a headless storefront must mount its thank-you route. Both checkouts return this way, the legacy one included (see LegacyHandoff); the handle to confirm the order by is the one start() remembered either way. hash is a legacy integrity token and is returned for completeness only; treat the order as real once confirmation says completed.
Parameters
Returns
HostedCheckoutReturn | null
pollConfirmation()
Poll until the order exists, the payment terminally fails, or the caps are reached. This is what a thank-you page should call. A timeout outcome is NOT a failure — the order may still land. Show “we are still processing your order” and let the customer refresh. On a legacy handoff timeout is the NORMAL outcome for a shopper who has not paid yet, because that checkout never reports failed; do not present it as an error there.
Parameters
Returns
Promise<ConfirmationOutcome>
rememberedStoreId()
The shop’s numeric store id as remembered from the last checkout session or cart this client saw, or null when nothing is known yet — the value the return leg needs after the cart is gone (a purchase event’s dedup key, hostedUrl() without a cart). Synchronous and request-free, like hostedUrl(); see SessionStore.peekStoreId for what “remembered” covers. The session’s copy is consulted first because that is the one still alive on a thank-you page.
Returns
string | null
resume()
Frame an EXISTING session — the redirect break-out’s landing. A redirect payment method (Klarna, Swish, iDEAL, a full-page 3-D Secure) takes the whole window to the PSP and comes back to the checkout’s own origin, which bounces to the page that created the session with ?qb_checkout_session=<id> on it. This is what that page calls with the id: no handoff, no new session, straight back into the frame the shopper was already in, which resumes at its confirmation step. <qb-checkout> and <Checkout> do this for you. Reach for it directly only when you route that parameter yourself. The URL is built locally, because there is no handoff call to ask. Pass storeId — the return URL carries it as qb_checkout_shop, next to the session id (readReturnedCheckout() reads both) — and no storage is involved at all. Without it the id remembered with the session, then the one off the last cart, is used; only when none of the three exists does this reject. Degrades the way mount does when the frame never answers: the hosted checkout URL for the same session is built from the same inputs, and a handshake-timeout sends the shopper there (unless fallbackRedirect: false). A return leg that lands on the wrong origin — a www redirect between leaving and coming back — thus still reaches the confirmation, on the hosted checkout, instead of an empty box.
Parameters
Returns
Promise<EmbeddedCheckout>
start()
Cart → session → handoff URL, in one call. The 90%-case entry point for a “Checkout” button. Returns the URL rather than navigating, so it works unchanged in a server action (redirect(url)), a route handler (302) and the browser (location.assign). The URL’s store id comes from the cart itself — the only place the storefront API states the shop’s numeric id. On a server client (one built with a cookies accessor) the shopper’s consent decision is forwarded on the URL too, read from the request’s qb_consent cookie: consentCategories, and the GA client and session ids when analytics is granted and the accessor has getAll(). That is what lets the hosted checkout set Consent Mode before its own tags load. consent: false in the config turns it off. In a browser the React hooks and the elements decorate the URL from the live consent store instead.
Parameters
Returns
Promise<StartCheckoutResult>
syncCart()
Re-read the cart into a session whose snapshot has gone stale. Session creation is idempotent on cartId server-side: a second POST /checkout/sessions for the same cart returns the FIRST session, complete with the cart as it looked back then. That is right for a retry and wrong for a shopper who went to checkout, came back, added something, and set off again — their new lines would never reach the checkout, with no error anywhere to say so. Patching cart: {} is the platform’s own “rehydrate from the Cart API” instruction, and is what the hosted checkout does when the shopper edits quantities inside it. Only patches when the snapshot actually differs, so a session the shopper is midway through is not needlessly disturbed. Pass cart when you already have it (start() does) and the check costs no request at all.
Parameters
Returns
Promise<CheckoutSessionSnapshot | null>

ConsentResource

The shopper’s consent decision as this client sees it, and the settings every part of the kit has to agree on to read it. Not a request to the platform: the decision lives in the qb_consent cookie on the storefront’s own origin, so on a server it is read through the same cookies accessor the cart id is, and in a browser from document.cookie.

Constructors

Constructor
Parameters
Returns
ConsentResource

Accessors

cookieName
Get Signature
The cookie the decision is stored in.
Returns
string
enabled
Get Signature
False when the config says consent: false.
Returns
boolean
revision
Get Signature
The cookie-policy revision a stored decision must match.
Returns
number

Methods

decorateHandoff()
A hosted checkout URL with the decision from this client’s cookie accessor on it (consentCategories, plus gaClientId / gaSessionId when analytics is granted and the accessor has getAll()). What checkout.start() applies on a server client. The URL comes back untouched when consent is off or the client has no cookie accessor: a browser client’s handoff is decorated by the React hooks and the elements instead, from the live consent store.
Parameters
Returns
Promise<string>
read()
The decision, from this client’s cookies: the cookies accessor when one was configured, else document.cookie, else undecided. A decision made under another revision, or a cookie that is not one, reads as undecided.
Returns
Promise<ConsentState>

ConsentStore

The shopper’s consent decision, observable. One per page, shared by the banner, the “Cookie settings” link in the footer, every <ConsentGate> and the analytics hub — the same shape as CartStore (subscribe / getSnapshot, an immutable snapshot replaced on every change) so useSyncExternalStore and the elements’ bindings can read it the same way they read the cart. The decision is the cookie’s; this object is a view over it that can also write. Reading happens once, at construction (or never, with initialState), and every write goes through save() — there is no second source of truth to drift from.

Constructors

Constructor
Parameters
Returns
ConsentStore

Properties

getSnapshot()
Returns
ConsentSnapshot
revision
subscribe()
Parameters
Returns
Returns
void

Accessors

pristine
Get Signature
True while the store still holds the state it started from: no decision saved, reset or adopted since. A provider uses it to tell “nothing known yet” from “the shopper (or something) has spoken” before applying a state of its own.
Returns
boolean

Methods

acceptAll()
Returns
void
allows()
Whether a category may be used right now. necessary always may; the others only after an explicit yes — undecided is a no.
Parameters
Returns
boolean
closeSettings()
Returns
void
getState()
The decision alone, without the UI flag — what gets persisted and forwarded.
Returns
ConsentState
hydrate()
Adopt a state read elsewhere — the server’s, or one re-read from a raw cookie value. Does not persist and does not announce: this is catching up with a decision, not making one.
Parameters
Returns
void
openSettings()
Returns
void
rejectAll()
Necessary only. As prominent a choice as “accept all”, by law.
Returns
void
reload()
Re-read document.cookie, for a page that knows another tab decided.
Returns
void
reset()
Withdraw consent: forget the decision and ask again. Withdrawal must be as easy as consent, so this is one call — the banner reappears, Consent Mode is updated to denied, and nothing optional fires until the next decision. Scripts already on the page are not unloaded (nothing can unload them); they simply receive nothing more.
Returns
void
save()
Record a decision. Persists it (unless persist: false), closes the preferences panel and announces it on document as qb:consent, so a page’s own scripts can react the way the hosted storefront’s do.
Parameters
Returns
void

EmbeddedCheckout

A checkout rendered inside the merchant’s own page, and the conversation with it. The iframe document is always served from the checkout’s own origin, so the PSP integration, the same-origin API proxy and the wallet domain registrations keep working exactly as they do on the redirect path. What changes is who owns the window: the frame may not navigate anything, so every exit — the back link, a PSP redirect, the completed order — arrives here as a message and is performed by this host. A handle is also returned when there is nothing to embed (see EmbeddedCheckoutFallbackReason). That is not an error and does not throw: it is an expected shop configuration, reported as a fallback event on a handle whose state is "fallback". An embed handle whose frame never answers ends up in the same place: the frame is removed, state flips to "fallback", and the same event carries the hosted checkout URL — so one listener covers every way the shopper can end up on the hosted checkout.

Constructors

Constructor
Parameters
Returns
EmbeddedCheckout

Properties

sessionId
The session being checked out, when one is known.

Accessors

iframe
Get Signature
The frame, or null in a fallback handle.
Returns
HTMLIFrameElement | null
state
Get Signature
"embed" while framed, "fallback" when there was nothing to frame — or when the frame there was never answered and has been taken down.
Returns
"fallback" | "embed"

Methods

cartUpdated()
Tell the frame that the cart it is checking out has changed here. The session’s cart is a snapshot taken when the session was created, so a page that lets the shopper edit the cart while the checkout is on screen — its own quantity steppers, an “add a gift box” button, a cross-sell strip — has to say so, or the frame goes on showing (and charging for) the cart as it stood a moment ago. The frame re-reads the cart from the platform and re-prices everything that hangs off it; nothing about the new cart travels in this message. <qb-checkout> and React’s <Checkout> call this for you, off the kit’s own cart store. Call it yourself only when you drove mount() by hand, or when you change the cart through something other than Quickbutik.cart — and then only after the change has actually been saved, because the frame reads the server’s cart, not yours. Held — not dropped — before the frame has said ready, and ignored by a fallback handle: there is no frame to tell, and there never will be. It is cheap and safe to over-call — the frame coalesces a burst into at most one extra read. Holding it is the whole point. The frame does NOT re-read the cart as it boots: the session’s cart is a snapshot taken when the session was created, and this message is the only thing that refreshes it (see use-cart-rehydrate.ts in checkout-frontend, and EMBED_CONTRACT §3). The window between the frame being built and its first ready is the iframe load plus the frame’s own session fetch — seconds on mobile — and a mutation dropped in it would be a shopper charged for the cart as it stood before they changed it. One flag, not a queue: the message carries nothing, so two of them say exactly what one says.
Returns
void
destroy()
Remove the frame and every listener. Idempotent, and safe to call before the handshake completes — which is what React’s StrictMode double-mount does on every development render.
Returns
void
focus()
Ask the frame to focus its first control.
Returns
void
off()
Type Parameters
Parameters
Returns
void
on()
Subscribe. Returns an unsubscribe, so a one-liner needs no off.
Type Parameters
Parameters
Returns
Returns
void

ProductController

The variant-selection state machine for one product, with no framework attached. It owns nothing but a VariantSelection — every field on the snapshot is derived from (product, selection) by the pure functions in variant-matrix.ts. That is deliberate: the matrix is the only place variant logic lives, and both bindings over it (React’s useProductState, the <qb-product> element) are glue. Built for useSyncExternalStore, hence the cached snapshot: that hook compares snapshots by identity and would loop forever on a freshly-allocated object. The same caching is what lets the elements layer skip re-binding the DOM when nothing changed.

Constructors

Constructor
Parameters
Returns
ProductController

Properties

getSnapshot()
Returns
ProductSnapshot
subscribe()
Parameters
Returns
Returns
void

Accessors

product
Get Signature
Returns
Product

Methods

clear()
Unset one group’s choice.
Parameters
Returns
void
reset()
Unset every choice.
Returns
void
select()
Choose a value. Contradicting choices are cleared, not refused.
Parameters
Returns
void
selectVariant()
Jump straight to a variant, e.g. from a ?variant= deep link.
Parameters
Returns
void
setProduct()
Point the controller at a different product, resetting the selection. A quick-view that swaps items reuses one controller, and the previous product’s option ids are meaningless against the new one — keeping them would resolve a variant that does not exist. Re-selecting the SAME product is a no-op, so a re-render that happens to pass an equal object does not throw away what the shopper picked.
Parameters
Returns
void

ProductsResource

The shop’s public catalog. Reads hit api-core’s storefront controller, which serves only visible products and omits everything a shopper has no business seeing (cost prices, suppliers, internal notes). There is no write surface here on purpose: a publishable key cannot create or edit products.

Constructors

Constructor
Parameters
Returns
ProductsResource

Methods

get()
A single product by its prefixed (prod_123) or numeric id. Returns null when it does not exist or is not visible. A bare number is prefixed on the way out — the gateway validates the path param against ^prod_\d+$ and 400s a plain 27. See normalizePrefixedId.
Parameters
Returns
Promise<Product | null>
getBySlug()
Find a product by its URL slug. This walks pages until it matches. The storefront product list accepts only limit/offset — there is no ?slug= filter to push the lookup server-side — so the cost grows with the catalog. That is fine for a small shop and for build-time generation, and wrong for a hot request path on a large one. Two ways to avoid it:
  • cache the result per slug (in Next.js, wrap the call in cache() and set a revalidate window), or
  • build a slug → id map once at deploy time from listAll and look the id up with get, which is a single request.
Matching is case-insensitive and ignores surrounding slashes, so a slug taken straight from a route param works. Returns null when nothing matches. Every page it walks is priced for the same campaign — the client’s, or storefrontId — so the product it returns carries that campaign’s prices.
Parameters
Returns
Promise<Product | null>
list()
One page of products, newest configured order first.
Parameters
Returns
Promise<Page<Product>>
listAll()
Walk every page. Convenience for build-time generation (static params, sitemaps) — not something to call while rendering a request.
Parameters
Returns
Promise<Product[]>
search()
Filter, sort and page the catalog server-side. This is the method to reach for when building a real listing. list() offers nothing but limit/cursor, which forces a storefront to read the whole catalog and filter in its own process — fine for a handful of products, wrong the moment there are thousands.
Two things it deliberately does not do:
  • No facet counts. The gateway rebuilds every paginated response into { data, has_more, next_cursor }, so an aggregate could not survive the trip even if the server sent one. Derive facet values from categories.list(), or keep your own index.
  • No option-value filter. Option values are per-product rows, so “Svart” is a different id on every product and a cross-catalog filter would have to match on an unindexed name column.
Only visible products are ever returned, and — unlike list() — visibility is applied in the query rather than after paging, so pages come back full.
Parameters
Returns
Promise<Page<Product>>

SessionStore

What shopkit remembers between requests, and nothing else: the cart id, the checkout session id, and the numeric id of the shop — twice. Deliberately minimal: all of them are opaque server-side handles, so a leaked or stale value costs at most a fresh cart. No prices, no PII and no credential is ever persisted — that is what keeps a cookie-based store safe to use without consent banners in most jurisdictions (it is strictly necessary functionality) and what makes “just clear it” a valid recovery. The store id is the odd one out. shopkit never needs it to talk to the API (the publishable key already scopes every request to one shop), but it is the one value the hosted checkout URL is built from, and the API only ever states it on a cart. Remembering it off every cart that passes through is what lets checkout.hostedUrl() stay a synchronous, request-free call. It is kept twice because the two copies are written at different times and expire on different clocks. The copy next to the cart id is refreshed off every cart and expires with the cart’s TTL; the copy next to the checkout session id is written on handoff and goes with the session (clearCheckoutSessionId), so the return leg of an embedded checkout can build /embed/{storeId}/{sessionId} from a value no cart read disturbs. Neither copy is removed when the cart is FORGOTTEN: the platform drops the cart the moment an order is created, which on a redirect payment (Klarna, Swish) is normally before the shopper is back on the page, and the thank-you page then keys its purchase event on the id (checkout.rememberedStoreId()). A reload of that page, after a header badge has found the cart gone and the completed confirmation has forgotten the session, must still find the same value or the order is counted twice. Only clear() forgets it.

Constructors

Constructor
Parameters
Returns
SessionStore

Properties

storage

Methods

clear()
Forget everything: the cart, the session and both copies of the store id.
Returns
Promise<void>
clearCartId()
Returns
Promise<void>
clearCheckoutSessionId()
Forget the checkout session, and the store id remembered with it.
Returns
Promise<void>
clearStoreId()
Forget the cart’s copy of the store id. The checkout’s copy stays. Only clear() calls this: forgetting a cart keeps the id (see the class doc).
Returns
Promise<void>
getCartId()
Returns
Promise<string | null>
getCheckoutSessionId()
Returns
Promise<string | null>
getStoreId()
The remembered numeric shop id, or null. Also primes peekStoreId.
Returns
Promise<string | null>
peekCheckoutStoreId()
The checkout session’s store id without waiting on storage — the second fallback of hostedUrl() and embedUrl(), after the cart’s copy. Same rules as peekStoreId.
Returns
string | null
peekStoreId()
The remembered store id WITHOUT waiting on storage — what the synchronous hostedUrl() reads. Answers from memory first (every cart the client received set it), then from storage when the adapter answers synchronously — memory, localStorage, document.cookie and a request cookie jar all do, which is what carries the value across page loads and server requests. An adapter that only answers with a promise (Next’s cookies()) cannot be consulted here; until a cart has been read in that request the answer is null and the caller has to be told the id explicitly.
Returns
string | null
setCartId()
Parameters
Returns
Promise<void>
setCheckoutSessionId()
Checkout sessions are short-lived by nature (one purchase attempt), so they get a much shorter TTL than the cart — a day is generous for a shopper who wanders off mid-payment and comes back. The stored value is the HANDOFF handle, which is a checkout-v2 session id for a shop on the current checkout and a legacy order uuid for a shop on the old one (see StartCheckoutResult.handoffId). Both are v4 UUIDs and both are what checkout.confirmation() takes, so nothing downstream has to tell them apart — but do not assume a stored value addresses a session. storeId is the shop’s numeric id the session’s URLs are built from, remembered alongside for as long as the session is (see the class doc). Best-effort, like setStoreId: the session id is the value nothing else can recover, so that write is the one allowed to fail loudly.
Parameters
Returns
Promise<void>
setStoreId()
Remember the shop’s numeric id, noted off every cart. Written with the cart’s TTL, and kept when the cart is forgotten (see the class doc). Best-effort on the storage side. This runs on every cart READ, and a cart page must not fail because its cookie jar is read-only (an RSC) or full. The in-memory copy is kept regardless, so hostedUrl() works for the rest of the request either way. Storage is only written when the value is new, so a server request that merely reads the cart does not emit a cookie.
Parameters
Returns
Promise<void>

ShopkitApiError

A non-2xx response from the Quickbutik API.

Extends

Constructors

Constructor
Parameters
Returns
ShopkitApiError
Overrides
ShopkitError.constructor

Properties

body
Parsed response body when it was JSON, else the raw text, else null.
details
details / context from the platform’s error envelope, when present.
status

Accessors

retryable
Get Signature
True for the statuses where retrying the same request can plausibly help.
Returns
boolean

Methods

fromResponse()
Parameters
Returns
Promise<ShopkitApiError>

ShopkitClient

A configured Quickbutik storefront client. One instance is cheap and stateless apart from the remembered ids (cart, checkout session, the cart’s store id), so create one per request on the server (the cookie accessor belongs to that request) and one per app in the browser.

Constructors

Constructor
Parameters
Returns
ShopkitClient

Properties

cart
categories
checkout
consent
The shopper’s cookie-consent decision, as this client’s cookies carry it, and the settings (revision, cookieName) from the config. On a server: await shopkit.consent.read(), handed to <ShopkitProvider> as consent={{ initialState }} so the banner is right on the first paint.
products
runtime
scopes
shop
shopPrefix
The shop’s storage prefix (101928Y), decoded from the publishable key. It is the folder the shop’s files live under on the CDN — https://cdn.quickbutik.com/images/<shopPrefix>/products/<file> — and the value to build an imageBaseUrl from. It is NOT the shop’s numeric id: the hosted checkout URL wants that one, and it is Cart.storeId (checkout.start() reads it from the cart for you).
storefront
The campaign storefront this client sells into, with the surface defaulted — or null for an ordinary shop client. Set via ShopkitConfig.storefront.

Accessors

apiUrl
Get Signature
The commerce API origin this client talks to.
Returns
string
checkoutUrl
Get Signature
The hosted checkout origin shoppers are handed off to.
Returns
string
currency
Get Signature
The currency this client browses in — the shopper’s remembered choice, else ShopkitConfig.currency, else null (the shop’s own currency). It is what is REQUESTED, not necessarily what comes back: a currency the shop does not offer falls back to the shop’s, so format prices with product.currency / cart.currency, and use describeCurrency() with shop.get() to know the mode. Synchronous: answered from memory, or from storage when the adapter can answer synchronously (memory, localStorage, cookies, a request cookie jar). With an async-only adapter (Next’s cookies()) this reads the configured default; requests still consult the adapter.
Returns
string | null
defaultCurrency
Get Signature
The currency this client was configured with, ignoring any choice.
Returns
string | null
imageBaseUrl
Get Signature
Fallback image base, or null when images are rendered from the absolute url the API sends (the normal case). See ShopkitConfig.imageBaseUrl.
Returns
string | null
shopId
Get Signature
Deprecated
Renamed to shopPrefix in 1.0.0-beta.4, because that is what the value is: the shop’s storage prefix from the key. It is NOT the numeric id the hosted checkout needs — for that use cart.storeId, or let checkout.start() read it from the cart. Removed before 1.0.0.
Returns
string
storage
Get Signature
The underlying storage adapter, for tests and for advanced integrations.
Returns
StorageAdapter

Methods

onCurrencyChange()
Be told when currency changes through setCurrency(). Returns an unsubscribe function. The React hooks and the custom elements use this to refetch prices and refresh the cart.
Parameters
Returns
Returns
void
setCurrency()
Browse in another currency: remember it, and notify onCurrencyChange subscribers so a UI can refetch.
The cart is unchanged — it is priced per request, so the same cart reads in the new currency next time. Throws a ShopkitConfigError for a value that is not a three-letter code; a code the shop does not offer is accepted and simply resolves to the shop’s currency server-side.
Parameters
Returns
Promise<void>
withStorage()
A copy of this client bound to different storage — the idiomatic way to reuse one long-lived configuration across many server requests, each with its own cookie jar.
Parameters
Returns
ShopkitClient

ShopkitConfigError

A bad createShopkitClient config — a malformed key, a missing apiUrl.

Extends

Constructors

Constructor
Parameters
Returns
ShopkitConfigError
Overrides
ShopkitError.constructor

ShopkitError

Base class for everything shopkit throws, so catch can narrow on one type.

Extends

  • Error

Extended by

Constructors

Constructor
Parameters
Returns
ShopkitError
Overrides

ShopkitNetworkError

The request never produced a response (offline, DNS, abort, timeout).

Extends

Constructors

Constructor
Parameters
Returns
ShopkitNetworkError
Overrides
ShopkitError.constructor

Properties

cause
Overrides

ShopkitScopeError

A call was made that the client’s declared scopes do not cover. Thrown locally, BEFORE the request goes out, so the developer sees the missing scope by name instead of an opaque 403 from the gateway.

Extends

Constructors

Constructor
Parameters
Returns
ShopkitScopeError
Overrides
ShopkitError.constructor

Properties

declared
required

ShopResource

Shop-level presentation data: name, logo, brand colour, default language, terms link. Everything a storefront shell needs before it renders.

Constructors

Constructor
Parameters
Returns
ShopResource

Methods

get()
Served by the checkout’s shop endpoint — the only shop surface a publishable key can reach, hence the checkout:read scope for what looks like plain branding.
Parameters
Returns
Promise<Shop>

Transport

The one place a request to Quickbutik is made. A thin adapter over @quickbutik/sdk-gateway, the platform’s own API client, which is bundled into this package rather than depended on — it is published to a private registry, so a third-party storefront could not install it. Everything underneath comes from there: bearer auth derived from the publishable key, the QB-Version header, per-attempt timeouts, exponential-backoff retries that honour Retry-After, adaptive rate-limit pacing driven by the gateway’s X-RateLimit-* headers, and an automatic Idempotency-Key on every mutation (so a retried POST /cart can never create two carts). shopkit behaves like every other Quickbutik client instead of re-implementing all of that. What stays here is shopkit’s own contract: mapping the platform’s error taxonomy onto shopkit’s, treating a vanished resource as null, unwrapping api-core’s response envelope, and adopting a rotated publishable key.

Constructors

Constructor
Parameters
Returns
Transport

Methods

request()
Send a request and return its body with any envelope removed.
Type Parameters
Parameters
Returns
Promise<T>
requestOrNull()
Like request but resolves to null on 404/410 instead of throwing — “not there” is a normal answer for a remembered id (a cart that expired, a session that was cleaned up), not an error a storefront should crash on.
Type Parameters
Parameters
Returns
Promise<T | null>
requestVoid()
Send a request whose response body is discarded (204 / plain OK).
Parameters
Returns
Promise<void>

Interfaces

AddCartItemInput

Properties


AnalyticsContext

What the hub knows when it calls a destination.

Properties


AnalyticsDestination

Where events go. The kit ships Google (gtag.js / GTM) and the Meta Pixel; anything else — TikTok, Klaviyo, a server-side collector — is one object that implements this.

Properties

Methods

load()?
Put the vendor’s script on the page. Called once, the first time the destination is allowed, and only in a browser. Must be idempotent: a second hub on the same page (two <qb-shop>s) calls it again.
Parameters
Returns
void
onConsent()?
The decision changed. Called after load(), on every change, allowed or not.
Parameters
Returns
void
track()
Parameters
Returns
void
trackPurchase()?
The purchase, with the id Meta and the platform dedupe it by (purchase_{storeId}_{orderNumber}). Optional: a destination without it receives the purchase through track() as a purchase event instead.
Parameters
Returns
void

AnalyticsOptions

Properties


ApiEnvelope

api-core wraps most responses in this envelope. The gateway strips it on some routes and forwards it on others, so the transport unwraps defensively rather than per-route (see unwrapEnvelope).

Type Parameters

Properties


AsyncSnapshot

Type Parameters

Properties


Cart

A server-owned cart. Prices are recomputed on every read, so a cart held in a cookie for a week still reflects today’s prices — never cache these numbers client-side beyond the current render.

Properties


CartItem

Properties


CartLineDiff

Properties


CartMutationEvent

A successful change made THROUGH this store, with the cart before and after — what an analytics layer diffs into add_to_cart / remove_from_cart. Never emitted for load, refresh or hydrate (nothing changed, something was read) nor for a failed mutation.

Properties


CartPresentment

See Cart.presentment.

Properties


Category

Properties


CategoryAncestor

One entry of Category.ancestors: a lightweight parent reference.

Properties


CategoryListParams

Properties


CheckoutCartProduct

A cart line as the checkout session snapshotted it.

Properties


CheckoutDataNodes

The data nodes shopkit types; the map carries others too.

Properties


CheckoutOrderTotal

The server-authoritative total: products + shipping + payment fee + discounts. This is the figure the shopper is charged — never recompute it.

Properties


CheckoutPricing

Product-only subtotals. order_total is the one that includes everything.

Properties


CheckoutSession

What POST /v2/checkout/sessions answers with.

Extended by

Properties


CheckoutSessionSnapshot

What GET /v2/checkout/sessions/:id answers with.

Properties


CommerceEvent

Properties


CommerceEventParams

Indexable

Properties


CommerceItem

A GA4 item. item_id is the bare numeric PRODUCT id, never the variant’s: it is what the hosted storefront’s tags emit and what the Google Shopping feed carries, so it is the only id that matches a catalogue on the other end. The variant rides along as item_variant. Prices are in MAJOR units.

Indexable

Properties


ConsentChoice

What a shopper chooses: the optional categories, on or off.

Extended by

Properties


ConsentCookieOptions

Extended by

Properties


ConsentCookieWriteOptions

Extends

Extended by

Properties


ConsentLabels

The copy of the default banner, in the languages Quickbutik shops sell in. The kit renders no markup of its own except where an empty element would be a bug, and an empty consent banner is one — so <ConsentBanner> and <qb-consent-banner> fill themselves with a minimal dialog when given no content, and this is its text. Every string is overridable; a storefront with its own voice passes labels.

Properties


ConsentModeSignals

The four Google Consent Mode v2 signals the platform sets. The mapping is the hosted storefront’s, byte for byte: analytics drives analytics_storage; marketing drives the three advertising signals. functionality_storage and personalization_storage are deliberately not set — the platform never has, and a storefront that wants them can push its own gtag('consent', …) call.

Properties


ConsentSnapshot

What the store exposes: the decision plus the one piece of UI state a banner and a far-away “Cookie settings” link have to share — whether the preferences panel is open. Not persisted.

Extends

Extended by

Properties


ConsentState

The persisted decision. Both flags are false while undecided, so a consumer that only reads the flags gets the safe answer without checking the status first.

Extends

Extended by

Properties


ConsentStoreOptions

Extends

Extended by

Properties


CookieAccessor

The three cookie operations shopkit needs from a server framework. Model it on whatever the host gives you — Next’s cookies(), a Hono context, an Express req/res pair. set/remove are optional: a read-only accessor (an RSC, which cannot mutate cookies) is a legitimate, common case. Writes are then dropped rather than throwing, so rendering a server component never crashes just because it touched the cart.

Methods

get()
Parameters
Returns
MaybePromise<string | null | undefined>
getAll()?
Every cookie on the request, as Next’s cookies().getAll() returns them. Optional, and read by one feature only: the checkout handoff looks for Google Analytics’ _ga / _ga_<property> cookies (whose names it cannot know in advance) to stitch the storefront’s GA session to the hosted checkout’s. Without it the consent decision is still forwarded and the GA ids are not.
Returns
MaybePromise<readonly object[]>
remove()?
Parameters
Returns
MaybePromise<void>
set()?
Parameters
Returns
MaybePromise<void>

CookieAttributes

Cookie primitives with no dependencies, usable on both sides of the wire. Kept deliberately small: shopkit only ever stores two opaque ids (the cart id and the checkout session id), so there is no need for signing, chunking or the full cookie package.

Properties


CreateSessionInput

Input to checkout.createSession(). successUrl is REQUIRED unless successMode is "inline"; a redirect session without one throws a ShopkitConfigError before any request. successMode is sent only when set.

Properties


CurrencyInfo

What a currency choice means for one shop. See describeCurrency.

Extended by

Properties


DataState

A server-computed value derived from the fields (prices, shipping options).

Type Parameters

Properties


DestinationsFromShopOptions

Properties


DisplayCurrency

The currency a checkout may show an APPROXIMATE amount in, next to the real prices — set when the session was created with a "display" currency.

Properties


EmbeddedCheckoutEventMap

Properties


EmbeddedCheckoutInitEmbed

Presentation and default-behaviour options, shared by mount and resume.

Extends

Properties


EmbeddedCheckoutInitFallback

Properties


EmbedRenderOptions

Presentation and default-behaviour options, shared by mount and resume.

Extended by

Properties


EmbedViewport

The host’s visible region, as the frame needs to see it. This is what lets a modal or a 3-D Secure overlay position itself inside the visible slice of a 2000px-tall auto-sized frame instead of at the frame’s own centre, which can be far off screen.

Properties


FieldState

A shopper-supplied value plus the server’s verdict on it.

Type Parameters

Properties


FormatMoneyOptions

Properties


The Google Analytics ids a page’s _ga / _ga_<property> cookies carry, for stitching the storefront session to the checkout’s. The regexes are the hosted storefront’s (cart.php), so both surfaces extract the same values from the same cookies.

Properties


GoogleTagOptions

Properties


HandoffParamsOptions

Properties


HostedCheckoutReturn

What the hosted checkout appends when it sends the shopper back after a completed order: /success/<orderNumber>?hash=…&t=….

Properties


HostedCheckoutUrlInput

Properties


ImageSrcSetOptions

Resize/encode parameters for Quickbutik’s image CDN (imgix-compatible). Every field is optional and omitted params fall back to the CDN’s own defaults. In a local/rig environment images are served straight from S3, which ignores these — the URLs stay valid, they just come back full-size.

Extends

Properties


ImageTransform

Resize/encode parameters for Quickbutik’s image CDN (imgix-compatible). Every field is optional and omitted params fall back to the CDN’s own defaults. In a local/rig environment images are served straight from S3, which ignores these — the URLs stay valid, they just come back full-size.

Extended by

Properties


LegacyHandoff

A legacy handoff, and what does and does not carry over from v2. The shopper comes back, the same way as from v2. After a completed order the legacy checkout redirects to <successUrl origin>/success/<orderNumber>?t=…&hash=… — only the ORIGIN of your successUrl is used, exactly as on v2, so one thank-you route serves both checkouts — and its back links go to your backUrl. CheckoutResource.parseReturnUrl reads that return, and the uuid start() remembered is what pollConfirmation() (and useOrderConfirmation() / <qb-order-confirmation> with no id given) confirms the order by. hash is computed the same way on both checkouts. The post-purchase-offer token (&ppo=) is only appended when successUrl is on a host the shop owns, so a headless storefront on its own domain should not expect one. Up to kit 1.3 this said the opposite — the legacy checkout used to redirect to the shop’s own platform storefront and could not be pointed anywhere else. The platform now honours the handoff’s successUrl and backUrl on both checkouts. Three things that still differ from v2:
  1. successMode: "inline" does not apply. The legacy checkout always redirects, so the platform refuses an inline handoff for a legacy shop with a 400 (details.reason: "legacy_checkout_unsupported") before any order is created. Pass a real successUrl.
  2. There is no session. getSession() and patching do not apply. Poll CheckoutResource.confirmation with legacyOrderUuid instead — the confirmation endpoint takes either handle.
  3. A failed payment reads as no_attempt, not failed. The legacy checkout leaves a failed order looking untouched, so a timeout or no_attempt outcome is not evidence the shopper gave up. Rely on the merchant’s own order confirmation email as the source of truth.
Cart-level campaign and säljplats override prices are also NOT carried over: the legacy checkout re-prices every line from the catalog. A shop that sells on campaign pricing needs checkout-v2. Minor, and outside the storefront’s control: the order records the IP the platform saw for the caller, which for a headless storefront is a platform address rather than the shopper’s. Merchant-visible on the order; it affects nothing about the handoff, the payment or the confirmation.

Properties


ListParams

Properties


MetaPixelOptions

Properties


MountCheckoutInput

Input to checkout.start(): a CreateSessionInput whose cart is optional. The successUrl rule is the same — required unless successMode: "inline".

Extends

Properties


NextLikeMetadata

Next.js Metadata, structurally. Declared here rather than imported from next: shopkit must not depend on a framework, and this is the subset buildSeo can fill. Assignable to Next’s own Metadata type at the call site.

Properties


OrderLine

A line the server added on top of the products (shipping, fee, discount).

Properties


Page

Cursor-paginated collection, as the gateway returns it.

Type Parameters

Properties


ParsedPublishableKey

Properties


PatchResult

What a PATCH /v2/checkout/sessions/:id answers with — the fields and data after the change, plus what the change invalidated.

Properties


PickProductImageOptions

Properties


PollConfirmationOptions

Properties


Product

A product as the storefront sees it — visible products only, no cost prices, no supplier data. Served by api-core’s storefront controller, which is the only products surface a publishable key can reach.

Properties


ProductControllerOptions

Properties


ProductImage

Properties


ProductImageUrlOptions

Resize/encode parameters for Quickbutik’s image CDN (imgix-compatible). Every field is optional and omitted params fall back to the CDN’s own defaults. In a local/rig environment images are served straight from S3, which ignores these — the URLs stay valid, they just come back full-size.

Extends

Extended by

Properties


ProductJsonLdOptions

Properties


ProductListParams

Which campaign storefront prices a product read. A client bound to a campaign (ShopkitConfig.storefront / storefrontId) sends its id on every product read, so the catalog it shows carries the same campaign prices its cart will charge — a product page that says 199 kr above a cart that says 149 kr is the bug this closes. Pass one here to price a single read for a different campaign; reads are stateless, so any campaign may be named. null opts a bound client out for this call and reads the shop’s ordinary prices.

Extends

Properties


ProductOptionType

A selectable option (“Size”), owned by the product.

Properties


ProductOptionValue

One concrete choice of an option (“XL”), referenced by a variant.

Properties


ProductReadOptions

Which campaign storefront prices a product read. A client bound to a campaign (ShopkitConfig.storefront / storefrontId) sends its id on every product read, so the catalog it shows carries the same campaign prices its cart will charge — a product page that says 199 kr above a cart that says 149 kr is the bug this closes. Pass one here to price a single read for a different campaign; reads are stateless, so any campaign may be named. null opts a bound client out for this call and reads the shop’s ordinary prices.

Extended by

Properties


ProductSearchParams

Which campaign storefront prices a product read. A client bound to a campaign (ShopkitConfig.storefront / storefrontId) sends its id on every product read, so the catalog it shows carries the same campaign prices its cart will charge — a product page that says 199 kr above a cart that says 149 kr is the bug this closes. Pass one here to price a single read for a different campaign; reads are stateless, so any campaign may be named. null opts a bound client out for this call and reads the shop’s ordinary prices.

Extends

Properties


ProductSection

A merchant-authored content section of a product page (“Size guide”, “Care”, “Delivery”), in the order the merchant arranged them. Rendered server-side exactly as the Quickbutik theme renders it: a section linked to a shared template already carries the template’s text, and the [STOCKLEFT], [PRICE] and [BEFOREPRICE] merge tags are replaced.

Properties


ProductSnapshot

Everything derived from one product plus the shopper’s current choices. The read half of ProductController, and the exact shape React’s ProductState is built from — the two must not drift, so the hook spreads this rather than restating it.

Properties


ProductVariant

Properties


ProductVariantPrice

Properties


ProductVariantStock

Properties


PurchaseEvent

The order a thank-you page reports, in major units.

Properties


RelatedProducts

Properties


RelatedProductSummary

A related product, reduced to what a product card needs.

Properties


RequestOptions

Extended by

Properties


ResolvedShopkitConfig

Config with every default applied and the key already parsed.

Properties


ResumeCheckoutInput

Presentation and default-behaviour options, shared by mount and resume.

Extends

Properties


ReturnedCheckout

What the checkout’s return route put on this page’s URL.

Properties


ScopeGuard

Properties

Methods

assert()
Throws ShopkitScopeError when scope is not covered.
Parameters
Returns
void
has()
Parameters
Returns
boolean

SeoBreadcrumb

Properties


SeoDefaults

Site-wide values every page shares. Supply once (via SeoProvider or as an argument) so a page only has to name what is specific to it.

Extended by

Properties


SeoImage

One image, as OpenGraph and schema.org want it.

Properties


SeoInput

What the page is about. Exactly one of these shapes is used.

Extended by

Properties


SeoOpenGraph

Properties


SeoRobots

Properties


SeoTags

Everything a page needs in <head>, as data. Deliberately a plain object rather than markup: Next’s App Router wants a Metadata export, Astro wants tags in its own layout, and a React 19 app can render them anywhere. One builder, three consumers.

Properties


SeoTwitter

Properties


SessionConfirmation

Properties


SessionEmbed

Where a checkout session may be embedded. The session is the trust anchor for framing: the party that creates it — the same party that already chooses successUrl — declares which single origin may put the checkout in an iframe, and the checkout emits exactly that origin as its Content-Security-Policy: frame-ancestors. A session created without this cannot be framed at all, by anyone. checkout.mount() fills both fields in for you from the current page. Pass them yourself only when the page that CREATES the session is not the page that will frame it — a server-side create for a storefront route, say.

Properties


SessionPrefill

Everything seeded at creation. A value the field rejects lands as status: "invalid" on that field — it never fails the create call, so a stale saved address can’t block a shopper from checking out.

Properties


SessionPrefillAddress

Properties


SessionPrefillCustomer

Known shopper identity, seeded into the session so the checkout prefills.

Properties


Shop

Shop-level presentation data a storefront needs before it can render anything: the name, the logo, the default language, the terms link. Served by the checkout’s shop endpoint, which is the only shop surface a publishable key can reach — shop settings are a merchant credential’s business, not a storefront’s.

Properties


ShopBrand

Properties


ShopBrandColor

Properties


ShopCurrency

One currency a shop offers.

Properties


ShopkitConfig

Properties


ShopkitConsentConfig

The consent settings every part of the kit has to agree on, kept on the client config so the server client, the browser provider and the checkout handoff all read them from the same object.

Properties


ShopkitRuntimeInfo

What the client resolved about its environment — handy when debugging.

Properties


ShopkitStorefrontConfig

ShopkitConfig.storefront — bind a whole client to one campaign.

Properties


StartCheckoutInput

Input to checkout.start(): a CreateSessionInput whose cart is optional. The successUrl rule is the same — required unless successMode: "inline".

Extends

Extended by

Properties


StorageAdapter

Where shopkit keeps the ids it must remember between requests: the cart id, the checkout session id and the cart’s numeric store id. Every method may return a promise. That is not decoration — Next.js 15’s cookies() is async, so a synchronous-only contract would lock the most common server integration out.

Properties

Methods

get()
Parameters
Returns
MaybePromise<string | null>
remove()
Parameters
Returns
MaybePromise<void>
set()
Parameters
Returns
MaybePromise<void>

StorageSetOptions

Properties


StorefrontAttribution

Per-call attribution. Accepted freely by the STATELESS calls — cart.create(), and checkout.createSession() / checkout.start() given an explicit cartId — where the caller owns which cart is involved. On the REMEMBERING calls (cart.ensure(), cart.add(), start() / buyNow() without a cartId) a storefrontId must match the client’s binding, because those calls reuse the cart the client remembers, and that cart was created for one campaign: a different id would be refused by the platform (storefront_mismatch), and on an unbound client it would leave a campaign-priced cart remembered as the shop’s ordinary one for thirty days. See assertRememberingCallBinding.

Properties


StorefrontBinding

The binding with the default applied — what ShopkitClient.storefront is.

Properties


TransportRequest

Properties


V2Handoff

A v2 handoff: an ordinary checkout session, plus which checkout answered.

Extends

Properties


VariantOptionGroupState

Properties


VariantOptionValueState

Properties


VariantPriceState

Properties

Type Aliases

AsyncStatus


BuyNowResult

What checkout.buyNow() resolves to: the StartCheckoutResult of the handoff, plus the cart the add produced. cart is the state BEFORE the shopper pays — the same basket the checkout was started from.

Type Declaration


CartCreateOptions

Options for the calls that may CREATE a cart: the request signal, plus the campaign storefront to bind the new cart to. On create() any campaign may be named; on ensure() / add(), which reuse the remembered cart, a storefrontId must match the client’s binding — see StorefrontAttribution.

CheckoutFlavour

Which checkout a shop actually runs.
  • "v2" — the current Quickbutik checkout. The handoff is a checkout session, and the whole SDK surface applies: getSession, patching, parseReturnUrl, the lot.
  • "legacy" — the older checkout. The handoff is an unpaid ORDER, not a session, and the platform creates it for you. See LegacyHandoff for what that costs you.
The shop decides this, not the storefront: it is whether checkout-v2 has been activated for that shop. It can differ between two shops using the same storefront code, so branch on it rather than assuming.

CheckoutHandoff

What POST /v2/checkout/handoff answers with, before the URL is resolved.

Type Declaration


CommerceEventName

The event vocabulary is GA4’s, because every destination understands it or can be mapped from it (the Meta destination translates), because it is what the hosted checkout already emits (<Checkout onEvent>), and because a GTM container reads it off the dataLayer with no extra configuration. The string escape hatch is for a storefront’s own events.

ConfirmationOutcome

Terminal outcome of polling. timeout is not a failure: order creation is asynchronous and may still land — render “we’re still working on it” rather than “your payment failed”.

ConfirmationStatus

  • completed — the order exists.
  • processing_payment — payment authorized, order being created (6–17s).
  • no_attempt — no payment attempt known for this session yet.
  • failed — the payment attempt failed terminally.

ConsentCategory

The consent model, kept to the three categories the rest of the platform speaks: the hosted storefront’s banner, the hosted checkout’s handoff parameter (consentCategories=analytics,marketing) and Google Consent Mode’s signals all map onto exactly these. A fourth category would be a non-breaking addition to the union, but nothing downstream would read it today, so there is none. necessary is never a choice: the cart id and checkout session id the kit stores are strictly necessary and need no consent (see SessionStore).

ConsentModeValue


ConsentStatus

  • undecided — no decision stored for the current revision. Everything optional is treated as denied until the shopper chooses.
  • decided — the shopper chose; analytics and marketing say what.

CookieList

Cookies as a list of pairs: what Next’s cookies().getAll() returns.

CountryCode

ISO 3166-1 alpha-2, uppercase — “SE”, “NO”, “DK”.

CurrencyCode

ISO 4217, uppercase — “SEK”, “NOK”, “EUR”.

CustomerType


EmbeddedCheckoutErrorCode

Error codes an error event can carry. All of them come from the frame today. The host raises none of its own: the one failure it can notice by itself — a frame that never answers — is not an error to the shopper but a reason to open the hosted checkout instead, so it travels as a fallback (see EmbeddedCheckoutFallbackReason).

EmbeddedCheckoutEventType


EmbeddedCheckoutFallbackReason

Why an embed handle is not actually embedding anything. The first three are decided before any frame exists; the last one after a frame was built and stayed silent.
  • legacy — the shop runs the legacy PHP checkout, which is not embeddable and will not be made so.
  • no-embed-url — the platform returned no embed URL for a v2 session.
  • no-top-navigation — this page cannot navigate its own top window (it is itself inside a sandboxed builder preview), so a redirect payment method could never break out of the frame.
  • handshake-timeout — the frame never said ready within 15 seconds, and has been removed. Almost always a frame-ancestors refusal: the page showing the frame is not the origin the session was created for (a wrong embedOrigin, www against the bare domain, a session resumed on another origin). A kit and a checkout that disagree on the message version look the same from outside. The browser reports either only inside the frame, so this is the host’s one way of noticing.

EmbeddedCheckoutHandler()

Type Parameters

Parameters

Returns

void

EmbeddedCheckoutInit


EmbedErrorCode

Error codes the FRAME can report. The host adds its own — see HostErrorCode.

EmbedFrameMessage


EmbedHostMessage


EmbedNavigateReason

Why the frame wants the host to leave the checkout.

EmbedRedirectMethod

How a PSP wants to be handed the shopper.

EmbedStep

The step the shopper is on inside the checkout.

FieldStatus


MaybePromise

Type Parameters


MinorUnits

Every money value crossing this API is an integer in the currency’s MINOR unit (öre, cents). Never a float, never a formatted string. Aliased so the intent is visible at every use site.

OptionalConsentCategory

The two categories a shopper actually decides on.

ProductCurrency

Every product states the currency its prices are in as product.currency (the client’s chosen currency when the shop offers it, else the shop’s own).

ProductLookup

Type Declaration


ProductSortField

Sort fields the storefront search accepts. createdAt is catalog order.

RelatedProductsMode

How the merchant chose a product’s related products:
  • category — products from the same category (the default);
  • specific — a hand-picked list, in the merchant’s order;
  • none — switched off for this product, or not used by the shop.

SchemaAvailability

schema.org availability, the vocabulary Google reads for rich results.

SeoOptions


SessionDataMap


SessionFieldMap


ShopCurrencyMode

How a currency is offered:
  • "base" — the shop’s own currency.
  • "display" — prices are SHOWN converted; the checkout charges the shop’s currency.
  • "charge" — priced AND charged in this currency.

ShopkitRuntime

Where is this code executing? browser means there is a real window AND a document — the two things the client-side storage adapters actually need. A worker has neither, so it is deliberately NOT a browser here even though it runs client-side code.

ShopkitScope


StartCheckoutResult

Where to send the shopper, and which checkout answered. url is present on both arms, so a “Checkout” button that only navigates needs no branching at all — and both arms return the shopper to <successUrl origin>/success/<orderNumber> after paying, so neither does the thank-you page. Branch on checkout when the difference matters — chiefly that a legacy handoff has no session to read or patch, and confirms by legacyOrderUuid. See CheckoutFlavour and LegacyHandoff.

Type Declaration


StoragePreference

How the SDK should persist ids.
  • "auto" (default) — in a browser: cookies when writable, else localStorage, else memory. On a server: the cookies accessor when one was supplied, else memory. Cookies win over localStorage in the browser on purpose: they are the only client-side store the SERVER can also read, which is what makes an RSC/SSR page see the same cart the browser has.
  • "cookie" / "localStorage" / "memory" — force one.
  • a StorageAdapter — bring your own (Redis, KV, signed cookie, …).

StorefrontRefusal

The three ways the platform refuses a campaign storefront checkout, decoded from the machine-readable details.reason beside the error message.
  • storefront_not_live (409) — the campaign is not open right now. effectiveState says why: paused, expired, coming_soon (or, exceptionally, draft / archived). Show the campaign-closed treatment; there is nothing for the shopper to fix.
  • storefront_quantity_limit (409) — the cart asks for more of a campaign line than the merchant allows. limitType decides the recovery: per_order means “reduce the quantity to at most maxQuantity”, pool means the campaign’s dedicated stock is down to maxQuantity (possibly 0 — sold out). productId is the prefixed id (prod_123) of the line.
  • storefront_mismatch (400) — the checkout named one campaign (storefrontId) but the cart was created for another (cartStorefrontId). A programming error, not a shopper condition: bind the client to the campaign, or check out a cart created for it.

StorefrontSurface


SuccessMode

  • "redirect" — the default. After payment the hosted checkout sends the shopper to <successUrl origin>/success/<orderNumber>; your thank-you page confirms the order with confirmation() / pollConfirmation().
  • "inline" — the hosted checkout renders the order confirmation itself and never redirects. successUrl is optional; parseReturnUrl() never fires; the storefront learns of completion only by polling the confirmation. backUrl is what brings the shopper home — set it.
The platform echoes the mode back as successMode on the session, the session snapshot and the confirmation. A platform without inline support rejects a session that has no successUrl with a 400, so keep passing successUrl until inline mode is verified on your shop.

VariantSelection

Which option value is chosen in each option group, keyed by option id. Partial by nature: a shopper who has picked a colour but not a size has one entry. A variant is only resolved once every group has one.

Variables

ANALYTICS_SCRIPT_ATTR

Marks every script tag the kit’s destinations add, so a page (or a test) can find them.

CHECKOUT_SESSION_PARAM

The parameter the checkout’s return route appends to the merchant’s page.

CHECKOUT_SHOP_PARAM

The shop’s numeric id, appended next to CHECKOUT_SESSION_PARAM. It is what lets the return leg build /embed/{storeId}/{sessionId} with no storage at all.
The DOM event dispatched on document after every decision.
The languages the default banner has copy for.
The default banner’s stylesheet, shared by <ConsentBanner> and <qb-consent-banner> so one set of rules (and one set of custom properties) styles both. Three rules about how it gets along with a site’s own CSS:
  • Everything sits in @layer qb-consent. Unlayered styles beat layered ones whatever their specificity, so any rule a site writes for a .qb-consent* class wins without !important or a longer selector.
  • The knobs are custom properties, read with a fallback, so setting one on :root (or on the banner) is enough: --qb-consent-bg, -fg, -muted, -border, -accent, -accent-fg, -radius, -button-radius, -font, -font-size, -padding, -gap, -offset, -max-width, -shadow, -focus, -z-index. The built-in colours follow prefers-color-scheme; a colour you set applies in both schemes.
  • It only matches the default markup, and not when that markup carries qb-consent--unstyled (the unstyled prop / attribute), so a page that wants none of it gets none of it.
Accept and reject share one look on purpose: an “accept all” more prominent than “only necessary” is exactly what EU regulators object to.

Pushed to the dataLayer on every consent change, for GTM triggers.

DEFAULT_API_URL

Quickbutik’s public commerce API host. Used when apiUrl is omitted. This is the storefront half of the platform API: it answers CORS preflights for any origin and accepts publishable keys only, so a browser can call it straight from the shop’s own domain with no proxy in between. The merchant API — personal access tokens, admin scopes, no wildcard CORS — is a separate host, https://api.quickbutik.com, and is not what this SDK talks to.

DEFAULT_CHECKOUT_URL

Quickbutik’s current hosted checkout (checkout-v2). Used when checkoutUrl is omitted. Up to 1.1.0 this named checkout.quickbutik.com, which is the LEGACY checkout — a different application that reads the last path segment as an order id, not a checkout session id, so every URL built from it answered “we couldn’t find it”. Shops on the legacy checkout are no longer served by guessing a host at all: checkout.start() asks the platform, which knows which checkout the shop runs and returns the matching URL. This constant is only the last-resort fallback for the checkout-v2 flavour.
The decision lives in a cookie, not in localStorage, for one reason: a server can read it. A storefront that renders on the server can then paint the page with the right banner state on the first byte — no banner for a shopper who already decided, no flash for one who has not — and a server action can forward the decision to the hosted checkout without a round trip through the browser. The format is the kit’s own (not the hosted storefront’s cc_cookie): the two cookies never share a hostname, and this one carries the revision and the timestamp a compliance review asks for.
Six months. Long enough that a returning shopper is not asked on every visit, short enough to re-collect within the window most EU regulators consider reasonable. Configurable per shop.

DEFAULT_SHOPKIT_SCOPES

The set every storefront needs to browse, build a cart and hand off to the hosted checkout. Matches the default of the platform’s publishable-key minting (create-token.ts --type publishable), minus storefront:read.

DEFAULT_STOREFRONT_SURFACE

The surface sent when a storefront is bound and none was named.

EMBED_MESSAGE_SOURCE

The discriminator. A page hosting a checkout also receives messages from Adyen, browser extensions and devtools; anything without this is not ours. Distinct from the admin preview’s qb-checkout-preview on purpose: the preview is a render target with no session and no network, the embed is the real checkout taking real money, and a message meant for one must never be readable as a message for the other.

EMBED_MESSAGE_VERSION

The only envelope version in existence. Both sides ignore an envelope whose version they do not implement, so a new kit against an old checkout (or the reverse) degrades to “the frame never becomes ready” — a handshake-timeout fallback after 15 seconds, which takes the frame down and opens the hosted checkout — rather than to a misread payload.

PUBLISHABLE_KEY_PREFIX

Prefix every Quickbutik publishable key carries.

SHOPKIT_SCOPES

The scopes a publishable key can usefully carry for a headless storefront, mirroring services/gateway/src/config/scopes.ts. Only this subset is reachable with an actorType: 'storefront' credential — the write scopes for products, orders and customers are merchant-only by design and a publishable key presenting them is rejected at the gateway.

Type Declaration


STOREFRONT_ID_PATTERN

sf_ + 26 characters of Crockford base32 (no I, L, O, U). Case-insensitive: the platform accepts either case and stores one canonical spelling.

STOREFRONT_SURFACES

Where a campaign storefront was presented to the shopper. Attribution, not behaviour.
  • hosted — the campaign’s own page on the shop’s domain.
  • link — a short or shared link that lands on the campaign.
  • embed — rendered inside a third-party page (iframe / script embed).
  • shopkit — inside a storefront built on this SDK. The default here.
  • agentic — reached by an AI agent acting for the shopper.

SUCCESS_MODES

How the hosted checkout ends: send the shopper back, or show the order itself.

Functions

absoluteUrl()

Resolve a possibly-relative URL against the storefront’s origin. Returns undefined rather than a relative value when there is no base: a relative <link rel="canonical"> is worse than none, because it resolves against whatever URL the crawler happens to be on.

Parameters

Returns

string | undefined

appendCheckoutHandoffParams()

The hosted checkout URL with the shopper’s consent — and, when analytics is allowed, the GA session link — appended the way the hosted storefront appends them: consentCategories=analytics,marketing, gaClientId, gaSessionId. The checkout reads them on arrival and sets Consent Mode before any tag loads. An undecided shopper is reported as consentCategories= (present, empty): a signal that nothing was granted, so the checkout runs denied. Only requireConsent: false omits the parameter, which makes the checkout track ungated. The legacy hosted checkout ignores these parameters. Idempotent: applying it twice leaves the URL the second call describes, so a URL a server client already decorated can be decorated again in the browser without stacking or contradicting itself. Returns the URL untouched when it cannot be parsed.

Parameters

Returns

string

applyImageTransform()

Add CDN transform parameters to an image URL, keeping any it already has. Exported for the case this module does not cover — a hero crop, an og:image built by hand — so the parameter names live in exactly one place.

Parameters

Returns

string

applyTitleTemplate()

Compose the document title. %s is the page title; the default appends the site name, and a page whose title already IS the site name is left alone so the homepage does not read “Min butik · Min butik”.

Parameters

Returns

string

bareProductId()

"prod_27" → "27"; a number passes through as its string.

Parameters

Returns

string
schema.org BreadcrumbList. Positions are 1-based and must be contiguous, so entries without a name are dropped before numbering rather than leaving a hole.

Parameters

Returns

Record<string, unknown> | undefined

buildImageSrcSet()

A srcSet for one image, or null when there is nothing to build one from. Density descriptors (the default) are right for an image rendered at a fixed CSS size — a thumbnail, a card at a known column width. Width descriptors plus sizes are right for one that reflows with the viewport.

Parameters

Returns

string | null

buildPriceState()

Price for the current selection: the exact figure once a variant is resolved, and the range across everything still reachable before that. Variant prices win over the product-level price, falling back to it when a variant carries none — which is how the platform models “all variants cost the same”.

Parameters

Returns

VariantPriceState

buildSeo()

Everything a page needs in <head>, from Quickbutik’s own data. Prefers what the merchant wrote — seoTitle / seoDescription are fields they filled in for exactly this purpose and are otherwise invisible to a headless storefront — and falls back to the product name and description.

Parameters

Returns

SeoTags

buildVariantMatrix()

The full picker state: every group, every value, and whether each is selected and still reachable.

Parameters

Returns

VariantOptionGroupState[]

canNavigateTopWindow()

True when this page can navigate its own top window. Three cases, and only the last is a problem:
  • this page IS the top window (the normal case) — yes;
  • it is framed by a document it can reach (same origin) — yes, and reading top.location.href proves it;
  • it is framed cross-origin, or sandboxed without allow-top-navigation — the read throws SecurityError, and a sandboxed frame could not navigate even if it could read.
The embedded checkout needs this: a redirect payment method takes the TOP window to the provider, so a page that cannot move its own top — an AI site builder’s preview pane, typically — must fall back to the redirect checkout rather than frame one the shopper could get stuck in. Probed rather than inferred, like hasLocalStorage above: the property exists in every case, and only touching it tells you the truth.

Returns

boolean

cartLineDiff()

What changed between two carts, as the items that were added and the items that were removed — with the QUANTITY that changed, not the line’s total. Keyed by product + variant, so a server that merged a second “add” into an existing line still yields “one more of this”, and a cart that went away (after: null) yields every line as removed.

Parameters

Returns

CartLineDiff

clearConsentCookie()

Expire the decision cookie. No-op outside a browser.

Parameters

Returns

void

clearOptionValue()

Remove one group’s choice, leaving the rest intact.

Parameters

Returns

VariantSelection

clearProductCache()

Forget a client’s cached lookups — one product’s, or all of them. Entries live as long as the client, which is right for a per-request server client and wrong for a long-lived browser session that must pick up an edited product. Call this after a revalidation. The client is required rather than optional: the cache is a WeakMap and cannot be enumerated, so a “clear everything” call would have to be a silent no-op.

Parameters

Returns

void

clearShopCache()

Forget a client’s cached shop, so the next read goes to the API.

Parameters

Returns

void

consentCategoriesParam()

The consentCategories value the hosted checkout reads off its URL. Three answers, and they mean different things to the checkout:
  • null — no consent signal at all. The checkout tracks ungated, exactly as it does for a hosted shop without the consent app. Only for a storefront that has opted out of consent (requireConsent: false).
  • "" — a signal, with nothing granted. Undecided is reported this way: the checkout must not track a shopper who has not said yes.
  • "analytics,marketing" — the granted subset.

Parameters

Returns

string | null

consentLabels()

The labels for a language, with overrides applied. lang is a BCP 47 tag or a bare code; only the primary subtag matters (sv-SE → sv), Norwegian’s no/nn fold into nb, and anything unknown falls back to English. Left out, it is read from <html lang> in a browser, so a storefront that already sets its document language gets the right copy for free.

Parameters

Returns

ConsentLabels

consentModeSignals()

The signals for a state. Null and undecided are everything denied.

Parameters

Returns

ConsentModeSignals

consumeReturnedSessionId()

Read and strip in one step. For a host built by hand that does not wait for the frame’s ready before deciding the return leg is over. The kit’s own surfaces no longer use it: <qb-checkout> and <Checkout> read with readReturnedCheckout on load and call stripReturnedSessionId from their ready handler, so a resume that fails before the frame answers leaves the parameters — and the reload that follows something to resume. Prefer that order. Not idempotent on purpose: the second call returns null. Hold the value rather than calling it again. Returns the session id alone, as it always has; read the shop id with readReturnedCheckout BEFORE calling this.

Returns

string | null

createCookieStorage()

Server-side cookie storage driven by an injected accessor.

Parameters

Returns

StorageAdapter

createDocumentCookieStorage()

document.cookie. Browser-only; readable by the server on the next request.

Parameters

Returns

StorageAdapter

createLocalStorage()

window.localStorage, with every access guarded (see hasLocalStorage).

Returns

StorageAdapter

createMemoryStorage()

In-process map. The always-available fallback; never survives a restart.

Returns

StorageAdapter

createRequestCookieStorage()

Read cookies straight off a Request (or any Headers), optionally collecting writes as Set-Cookie strings for the caller to attach to its response. Framework-free — the fit for Astro endpoints, TanStack Start server functions, plain fetch handlers and Workers.

Parameters

Returns

StorageAdapter

createScopeGuard()

Build the pre-flight scope checker. Passing scopes: null turns enforcement off — useful when the key’s scopes are decided by an admin and the app genuinely does not know them. The gateway still enforces; all that is lost is the friendly local error.

Parameters

Returns

ScopeGuard

createShopkitClient()

Create a storefront client.

Parameters

Returns

ShopkitClient

currencyDecimals()

Parameters

Returns

number

deleteDocumentCookie()

Expire one cookie in document.cookie. No-op outside a browser.

Parameters

Returns

void

describeCurrency()

Resolve a currency choice against what the shop offers — the same rule the platform applies: a code the shop does not offer (converter off, not enabled, no rate) silently falls back to the shop’s currency.

Parameters

Returns

CurrencyInfo

destinationsFromShop()

The destinations a shop’s tracking configuration asks for: Google when a GA4 measurement id or a GTM container is set, the Meta Pixel when a pixel id is. The ids are the merchant’s own, entered in the admin, already validated by the platform — and the same ones the hosted checkout loads, so the storefront and the checkout report into the same properties. Empty for a shop with nothing configured and for a platform older than the field. metaCapiEnabled is informational here: the Conversions API is sent by the platform for the hosted checkout’s own events, not from a storefront.

Parameters

Returns

AnalyticsDestination[]

detectRuntime()

Returns

ShopkitRuntime

firstAvailableVariant()

The first purchasable variant, for storefronts that preselect one.

Parameters

Returns

ProductVariant | null

formatMoney()

Minor units → a display string in the shopper’s locale. The React layer deliberately has no such helper: a hook hands back { amount, currency } and the app formats it however its design system says. HTML bindings have no such escape — data-qb-text="price.formatted" needs a string — so the elements layer must be able to produce one, and this is it.
The decimal count comes from currencyDecimals rather than a hardcoded 100, for the same reason the SEO builder needs it: JPY and ISK have no minor unit, and dividing those by 100 states a price that is a hundred times too small.

Parameters

Returns

string

formatMoneyRange()

A price range, or a single figure when both ends agree. What a “från 99 kr” label needs: VariantPriceState reports min, max and isRange, and every storefront then writes the same three-branch formatter.

Parameters

Returns

string | null

formatSchemaPrice()

Minor units → the decimal string schema.org and OpenGraph expect ("99.00"). A string, not a number: price in schema.org is text, and a float would reintroduce exactly the rounding error minor units exist to avoid.

Parameters

Returns

string

getOptionGroups()

The option groups for a product, in display order. product.options is optional on the wire, so the groups are reconstructed from the variants’ own option values when it is absent — otherwise a product that omits the top-level list would render no picker at all.

Parameters

Returns

ProductOptionType[]

getProductPromise()

The (cached) promise for one product lookup. A rejected promise is evicted so a retry — a remount after an error boundary reset, say — can actually re-request rather than replaying the failure forever. The original promise is still what the caller gets, so the rejection surfaces exactly once per attempt and never as an unhandled rejection.

Parameters

Returns

Promise<Product | null>

getShopPromise()

Parameters

Returns

Promise<Shop>

googleTagDestination()

GA4, Google Ads and Google Tag Manager, through one gtag and one dataLayer. Consent Mode v2 is set up BEFORE any script is injected: a denied default with wait_for_update, then — when the shopper has already decided — an update straight after. The pair matters: a merchant’s own GTM container ships region-scoped consent defaults applied at container init, and a default never outranks another default, so a lone granted default would be silently discarded and every Google tag would run cookieless. Only an update outranks a default. Events: with a GA4 measurement id (or an Ads id) they go through gtag('event', …), which a GTM container on the same page also sees. With a container ONLY, they are pushed as { event, ecommerce } with the GA4 ecommerce: null reset in between. Never both — a container that hosts a GA4 tag would otherwise count every event twice.

Parameters

Returns

AnalyticsDestination

hasDocumentCookies()

True when document.cookie is readable/writable — the browser half of the cookie story. The server half needs an injected accessor (see createCookieStorage), because there is no ambient request to read from.

Returns

boolean

hasLocalStorage()

True when localStorage is present AND usable. Presence alone is not enough: Safari private mode and “block all cookies” both expose the object and throw on write, and an iframe on a partitioned origin throws on mere access. The probe is the only reliable answer.

Returns

boolean

injectConsentStyles()

Put the default banner’s stylesheet in <head>, once per document. A no-op outside a browser and when it is already there. The React banner does not need this (it renders the same rules as a hoisted <style>, so they are in the server HTML too); it is for <qb-consent-banner> and for a page of your own that reuses the default class names.

Parameters

Returns

void

injectScript()

Add a vendor script to <head>, once per src for the lifetime of the page. A second call for the same URL — from a second hub, a re-configure, a React StrictMode double effect — is a no-op; so is a call outside a browser. The tag is async and carries data-qb-analytics="<id>". The CSP consequence is the storefront’s to carry: a page with a script-src must allow the vendor hosts it opts into (see the consent and analytics guide). The kit never evaluates strings.

Parameters

Returns

HTMLScriptElement | null

isBrowser()

True in a DOM-bearing browser context. Guarded with typeof on purpose: bundlers targeting server runtimes replace window with nothing at all rather than undefined, so a bare reference throws instead of returning false.

Returns

boolean

isSafeNavigationUrl()

Is this something the host may navigate to? Every url in the contract — navigate.url, redirect.url — is checked on both sides before any navigation, and this is the host’s copy of that check. Not defence in depth for its own sake: the frame validates what it sends, but the host is the party that actually performs the navigation, and it is the one holding the merchant’s origin. A javascript: URL handed to location.assign executes in THIS document — the merchant’s page, with the merchant’s cookies — so the check has to live where the navigation does. https: only, with http: allowed for loopback so a rig and a vite dev page work. Relative URLs are rejected rather than resolved: the contract says absolute, and resolving an attacker-chosen path against the host page is a more surprising outcome than dropping the message. This must agree with the frame’s copy EXACTLY, down to which hostnames count as loopback. A host that is stricter than the frame does not fail loudly — it drops a message the frame was happy to send, and a dropped message makes no sound at all. That lands on a developer running their storefront on http://app.localhost:5173, in local development, where the redirect break-out is the least likely thing to be exercised by hand. So the function below is a port of isLoopbackHostname in the frame’s embed-messages.ts, not an equivalent of it.

Parameters

Returns

value is string

isStorefrontId()

True for a well-formed campaign storefront id. Says nothing about whether it exists.

Parameters

Returns

value is string

isStorefrontSurface()

Parameters

Returns

value is “embed” | “link” | “hosted” | “shopkit” | “agentic”

itemFromCartLine()

Parameters

Returns

CommerceItem

itemFromProduct()

One GA4 item for a product (and the variant the shopper is looking at, when there is one). The price is the variant’s when known, else the product’s.

Parameters

Returns

CommerceItem

itemsFromCart()

The cart’s lines as GA4 items.

Parameters

Returns

CommerceItem[]

itemsFromCheckoutProducts()

A checkout session’s snapshotted lines (data.cart_products) as GA4 items.

Parameters

Returns

CommerceItem[]

itemsParams()

{ currency, value, items } for a set of items — the shape of every cart event.

Parameters

Returns

CommerceEventParams

metaPixelDestination()

The Meta Pixel, hard-gated on marketing: nothing — not the script, not PageView — happens before the shopper grants it, and nothing more is sent after they withdraw it. The event names are Meta’s standard events, mapped from the GA4 vocabulary the way the hosted storefront maps them: Meta’s own consent API follows the decision: fbq("consent", "grant") before init, and revoke / grant on every later change, so a withdrawal also stops the pixel’s cookies, not just the kit’s events. remove_from_cart and the shipping step have no Meta equivalent and are dropped. The purchase eventID is purchase_{storeId}_{orderNumber}, the id the platform’s own server-side events use, so Meta merges them; with no store id known the Purchase carries no eventID at all.

Parameters

Returns

AnalyticsDestination

normalizeApiUrl()

Strip trailing slashes and a trailing /v2. Every request path in this SDK carries its own /v2 prefix, so an apiUrl that already ends in /v2 would otherwise produce /v2/v2/... and 404 — a mistake operators make often enough that the platform’s own checkout normalizes for it too.

Parameters

Returns

string

normalizeCurrency()

"eur" / " EUR " → "EUR"; null, undefined and "" → null. Throws a ShopkitConfigError for anything that is not three letters: the platform rejects a malformed code with a 400, and a typo in a config is better reported here, by name, than as a failed product grid.

Parameters

Returns

string | null

organizationJsonLd()

schema.org Organization — the shop itself, for the knowledge panel.

Parameters

Returns

Record<string, unknown>

parseConsentCookieValue()

Decode one cookie value. Anything that is not a well-formed decision for the expected revision — garbage, a hand-edited value, a decision made against an older policy — reads as undecided, which is the safe answer.

Parameters

Returns

ConsentState

parseCookieHeader()

Parse a Cookie: header (or document.cookie) into a plain record.

Parameters

Returns

Record<string, string>

parseEmbedFrameMessage()

Validate a MessageEvent.data as a frame message, or return null. Null for anything that is not ours, anything whose envelope version we do not implement, and anything whose payload does not hold up — a navigate with no safe URL, a height that is not a number. The caller has already checked the sender’s origin and window; this checks the content, and the two together are the whole trust boundary.

Parameters

Returns

EmbedFrameMessage | null

parsePublishableKey()

Split qb_pk_<shopPrefix>_<random> into its parts. The platform mints publishable keys with the shop’s storage prefix in the third segment — the same value it uses in CDN paths — never the numeric shop id. A prefix may not contain _ (token minting enforces this, because _ is the field delimiter), so it is exactly the third segment.

Parameters

Returns

ParsedPublishableKey

pickProductImage()

One of a product’s images, in display order. position is the merchant’s ordering and is nullable, so this sorts by it with nulls last and falls back to the array’s own order — the same ordering the merchant sees in the admin.

Parameters

Returns

ProductImage | null

plainText()

Strip tags and collapse whitespace, then cap at a length a search engine will actually show. Product descriptions are merchant-authored HTML; dropping raw markup into a <meta> would put stray angle brackets in the snippet.

Parameters

Returns

string | undefined

productAvailability()

The most optimistic availability across a product’s variants.

Parameters

Returns

SchemaAvailability

productImages()

Every renderable image of a product, in display order.

Parameters

Returns

ProductImage[]

productJsonLd()

schema.org Product, the part that actually earns a rich result. Price and availability go in an Offer for a single-priced product and an AggregateOffer when the variants differ — Google reads both, and an AggregateOffer is the honest shape for “from 99 kr”. Offers are omitted entirely when no currency is known, because priceCurrency is required and guessing it would publish a wrong price.

Parameters

Returns

Record<string, unknown>

purchaseDedupKey()

qb_purchase_tracked_{storeId}_{orderNumber} — the localStorage key the hosted storefront’s success page and the hosted checkout’s inline confirmation also write, so no surface reports an order another already reported in this browser.

Parameters

Returns

string

purchaseEventId()

purchase_{storeId}_{orderNumber} — the platform’s Meta eventID for an order.

Parameters

Returns

string

purchaseFromSession()

The purchase a thank-you page reports, from the checkout session that produced the order: its snapshotted lines and the server-authoritative total. Null when the session carries neither (an older platform, or a session read before the data nodes resolved) — then the page has to build the purchase from its own data and call trackPurchase itself.

Parameters

Returns

PurchaseEvent | null

purchaseToEvent()

A purchase as the generic event every destination’s track can take.

Parameters

Returns

CommerceEvent

readConsentCookie()

Read the decision from a Cookie: header (or document.cookie). The server-side entry point: readConsentCookie(request.headers.get("cookie")) in a route handler, readConsentCookie(cookies().toString()) in Next.

Parameters

Returns

ConsentState

readDocumentCookie()

Read one cookie from document.cookie. Returns null outside a browser.

Parameters

Returns

string | null

readGaCookies()

Parameters

Returns

GaLink

readReturnedCheckout()

The session and shop the return leg put on the URL, or null when this is not a return leg. Reads this page’s URL by default. Pass a URL — a request’s, in a server component or a route handler — to read that one instead, which is what makes this usable before there is a window: the page that renders the return can decide server-side whether to render a checkout, and with which ids. Read-only: nothing is consumed, so it is safe to call during a render or from a framework’s router. The shop id is checked, not trusted: store ids are integers, and a query string is writable by anyone, so anything else reads as “not carried” rather than as a store id.

Parameters

Returns

ReturnedCheckout | null

readReturnedSessionId()

The session id on this page’s URL, or null. Read-only — nothing is consumed, so it is safe to call during a render or from a framework’s router. readReturnedCheckout returns the shop id alongside it; prefer that when the value is going into checkout.resume().

Returns

string | null

redactKey()

A key with its secret portion masked, safe to put in an error message or a log line. The shop prefix is kept: it is not a secret and it is the one part that makes a message actionable.

Parameters

Returns

string

resolveCategoryImageUrl()

The <img src> for a category’s cover image, or null when it has none. Same shape as resolveProductImageUrl: imageUrl is absolute and preferred, image is the bare filename kept for backwards compatibility.

Parameters

Returns

string | null

resolveConfig()

Parameters

Returns

ResolvedShopkitConfig

resolveLanguage()

Parameters

Returns

string

resolveProductImageUrl()

The <img src> for one product image, or null when there is nothing to show.
Null — rather than a broken URL — is returned for an image that has no file yet, so a caller can render its own placeholder.

Parameters

Returns

string | null

resolveStorage()

Turn a StoragePreference into a concrete adapter for the runtime we actually find ourselves in. Never throws and never returns null: an unusable preference degrades to memory rather than breaking the storefront. A degraded cart is a new cart — annoying; a thrown error during SSR is a blank page.

Parameters

Returns

StorageAdapter

resolveVariant()

The single variant a selection identifies, or null when the selection does not pin exactly one. Requires every group to be chosen — a partial selection is ambiguous even if it happens to match only one variant today, because that would make the resolved variant depend on the catalog rather than on the shopper’s choices. A product with no option groups resolves to its single variant, which is how a “simple” product behaves.

Parameters

Returns

ProductVariant | null

searchEvent()

Parameters

Returns

CommerceEvent

selectionForVariant()

The selection that identifies a given variant, for deep links and prefills.

Parameters

Returns

VariantSelection

selectOptionValue()

Apply a click on one option value, returning the next selection. Choosing a value that contradicts earlier choices clears the ones it contradicts rather than refusing the click. Picking Red when XXL is selected should give you Red with the size to re-pick — the alternative, a dead end where nothing is clickable, is the classic variant-picker trap. The just-clicked group is never cleared, so the shopper’s most recent intent always survives.

Parameters

Returns

VariantSelection

serializeConsentState()

The cookie value for a decided state. Throws on an undecided one: there is nothing to store, and clearConsentCookie is how “undecided” is written.

Parameters

Returns

string

serializeCookie()

Serialize one cookie into a Set-Cookie value.

Parameters

Returns

string

serializeJsonLd()

Serialize structured data for a <script type="application/ld+json">. Every < becomes \u003c, which is what stops a merchant-authored product name containing </script> from closing the tag and turning shop content into executable markup. A JSON parser reads the escape identically, so nothing is lost by being paranoid here.

Parameters

Returns

string

storefrontRefusal()

Narrow any thrown value to a campaign storefront refusal, or null when it is something else — a network error, a 404, an ordinary 409 (an out-of-stock line, say).
Matches on details.reason, not on the HTTP status, so the decoder does not have to know which status each reason travels on.

Parameters

Returns

StorefrontRefusal | null

stripReturnedSessionId()

Take the kit’s two parameters off the URL, leaving every other parameter and the hash where they were. Stripped at all because the session it names is spent the moment it is resumed: a shopper who reloads would otherwise re-mount a checkout for an order they have already paid for. Stripped only once the resumed frame has said ready — not on the pass that starts the resume — because a resume can fail before the frame ever answers: a document the browser refused, a store id nobody could supply, a handshake that timed out into the hosted checkout. With the parameters gone, a reload of that page would find no return leg and take the mount() branch; with them still there, the reload resumes again. The platform hands the same cart the same session back either way, so neither path can produce a second order — this is about a reload landing the shopper back in the checkout they paid in, not on a fresh one. replaceState, not pushState — the parameters were never a place in anyone’s history. A no-op when neither is present, so it is safe on every ready a frame sends, reloads included.

Returns

void

toMajorUnits()

Minor units → a number in major units (9900 SEK → 99, 990 JPY → 990). What GA4, Meta and every other analytics vendor want value and price in. Rounded to the currency’s own decimals so the float that comes out is the shortest exact representation (12.34, never 12.340000000000002). Without a currency two decimals are assumed, which is right for every currency the platform sells in by default.

Parameters

Returns

number

toNextMetadata()

Adapt SeoTags for a Next.js App Router generateMetadata export.
Structured data does not come along. Next’s Metadata has no slot for a <script type="application/ld+json">, so the jsonLd array has to be rendered in the page — <JsonLd data={tags.jsonLd} /> does it. Since that is where the rich-result value lives, it is the half not to forget; using <SEO /> instead emits both.

Parameters

Returns

NextLikeMetadata

undecidedConsent()

The state before any decision — and after a revision bump or a reset.

Parameters

Returns

ConsentState

unwrapEnvelope()

Strip api-core’s { data, message, statusCode } wrapper when present. Detection is structural rather than per-route on purpose: the gateway unwraps on some routes and forwards the envelope on others, and which is which has changed over time. A payload is treated as an envelope only when it has a data key alongside statusCode/message and nothing else of substance — so a genuine resource that happens to own a data field is never unwrapped, and neither is a paginated { data, has_more, next_cursor } collection.

Type Parameters

Parameters

Returns

T

variantAvailability()

Availability for one variant. stock: null means the shop does not track inventory for it — the same signal the variant picker treats as purchasable. Reporting OutOfStock there would hide every product of every non-tracking shop from rich results, so null is InStock. preorder wins when set, because a shopper can order it but it has not shipped.

Parameters

Returns

SchemaAvailability

variantMatchesSelection()

Whether variant carries every (optionId → valueId) pair in selection.

Parameters

Returns

boolean

viewItemEvent()

Parameters

Returns

CommerceEvent

viewItemListEvent()

Parameters

Returns

CommerceEvent

writeConsentCookie()

Write the decision to document.cookie. No-op outside a browser.

Parameters

Returns

void

writeDocumentCookie()

Write one cookie to document.cookie. No-op outside a browser.

Parameters

Returns

void