> ## Documentation Index
> Fetch the complete documentation index at: https://quickbutik.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Script tag

> Every data-* attribute of the script-tag build, the full window.Quickbutik API, and the Content Security Policy it needs.

<Info>
  This page is the curated reference. The script tag carries the SDK and the web components, so the generated references at [/kit/api/core](/kit/api/core) and [/kit/api/elements](/kit/api/elements) apply to it too. For a walkthrough, start with [Web components setup](/kit/web-components/setup).
</Info>

```html theme={null}
<script
  defer
  src="https://cdn.jsdelivr.net/npm/@quickbutik/kit@1.8.0/dist/quickbutik-kit.global.js"
  data-publishable-key="qb_pk_…"
  data-locale="sv-SE"
  data-consent-lang="sv"
  data-privacy-policy-url="/integritetspolicy"></script>
```

One self-contained file for a page with no npm and no build step. It reads its own `data-*` attributes, configures the ambient shop and registers the [web components](/kit/reference/elements), so the rest of the page is markup.

## The bundle

| | |
| - | - |
| File | `dist/quickbutik-kit.global.js` (also the `@quickbutik/kit/global` export, and the package's `unpkg` and `jsdelivr` entry) |
| CDN | `https://cdn.jsdelivr.net/npm/@quickbutik/kit@<version>/dist/quickbutik-kit.global.js` or `https://unpkg.com/@quickbutik/kit@<version>` |
| Size (1.8.0) | about 176 KB minified, about 51 KB gzipped |
| Global | `window.Quickbutik` |
| Target | ES2020, every browser with custom elements |
| Dependencies | None at runtime |

**Pin an exact version** in the URL and change it deliberately. Load it with `defer` to keep it off the critical path.

## Attributes

<ParamField body="data-publishable-key" type="string">
  The shop's `qb_pk_…`. Safe in a browser. Omit it and nothing is configured; the page then calls `Quickbutik.configure()` itself, which is the shape to use when the key comes from a template variable.
</ParamField>

<ParamField body="data-currency" type="string">
  The currency the page **browses in** (`EUR`) until the shopper picks another with `<qb-currency-select>` or `Quickbutik.setCurrency()`; a remembered choice wins over it. Sent with every product, cart and checkout request. Omit it to start in the shop's own currency. Prices format with the currency each response states, so a single-currency shop needs nothing here. See [Currencies](/kit/concepts/currencies).
</ParamField>

<ParamField body="data-locale" type="string">BCP 47, for number formatting. Defaults to the browser's.</ParamField>
<ParamField body="data-storefront-id" type="string">Sell the whole page into one campaign storefront (`sf_…`). See [Campaign storefronts](/kit/concepts/campaign-storefronts).</ParamField>
<ParamField body="data-api-url" type="string">Commerce API origin. Omit in production.</ParamField>
<ParamField body="data-checkout-url" type="string">Hosted checkout origin. Only for a preview environment or a checkout proxied onto your own domain.</ParamField>
<ParamField body="data-image-base-url" type="string">Fallback image base. Rarely needed; images arrive with absolute URLs.</ParamField>
<ParamField body="data-storage-key-prefix" type="string">Change it to run two shops in one browser.</ParamField>
<ParamField body="data-prefix" type="string" default="qb-">Tag-name prefix, for a collision with another library.</ParamField>
<ParamField body="data-no-auto-define" type="boolean">Load the library without registering the elements.</ParamField>

### Consent and analytics

The script tag shows a cookie banner and loads the merchant's own GA4, GTM and Meta pixel once the shopper agrees, **by default**. Nothing from Google or Meta loads before that. See [Consent and analytics](/kit/concepts/consent-and-analytics).

<ParamField body="data-consent" type="'false'">Turn consent off entirely: no banner, no analytics, nothing appended to checkout URLs.</ParamField>
<ParamField body="data-consent-lang" type="string">Language of the banner's built-in copy (`en`, `sv`, `da`, `nb`, `fi`). Defaults to `<html lang>`.</ParamField>
<ParamField body="data-privacy-policy-url" type="string">Adds a "Privacy policy" link to the banner.</ParamField>
<ParamField body="data-consent-banner" type="'false'">Mount no automatic banner. A `<qb-consent-banner>` you place yourself always wins anyway.</ParamField>
<ParamField body="data-consent-unstyled" type="boolean">The automatic banner without its stylesheet.</ParamField>
<ParamField body="data-consent-revision" type="number" default="1">The cookie-policy revision. Bump it to ask every shopper again.</ParamField>
<ParamField body="data-consent-cookie" type="string" default="qb_consent">The consent cookie's name.</ParamField>
<ParamField body="data-consent-domain" type="string">The consent cookie's domain, for example `.myshop.com` to share it across subdomains. Host-only by default.</ParamField>
<ParamField body="data-consent-max-age-days" type="number" default="180">The consent cookie's lifetime.</ParamField>
<ParamField body="data-analytics" type="'false'">Keep the banner, load no pixel.</ParamField>
<ParamField body="data-analytics-require-consent" type="'false'">Run analytics ungated, and make the hosted checkout track ungated too. Only for a storefront that owes its shoppers no consent.</ParamField>
<ParamField body="data-analytics-load-before-consent" type="boolean">Load GTM or gtag.js before the shopper decides, under a Consent Mode default of everything denied. A GTM container loaded this way runs all of its tags.</ParamField>

### A key from a server-rendered template

```html theme={null}
<script defer src="…/quickbutik-kit.global.js" data-no-auto-define></script>
<script type="module">
  Quickbutik.configure({
    publishableKey: window.SHOP_CONFIG.publishableKey,
    currency: window.SHOP_CONFIG.currency,
  })
  Quickbutik.defineElements()
</script>
```

`type="module"` matters: `defer` is ignored on an inline script, which would then run before the deferred kit script has defined `window.Quickbutik`. A module script runs after the deferred scripts before it.

## window\.Quickbutik

### The ambient shop

<ResponseField name="Quickbutik.shop" type="{ client, cartStore, currency, locale, analytics } | null">The ambient shop, or `null` when nothing is configured.</ResponseField>
<ResponseField name="Quickbutik.client" type="ShopkitClient | null">The whole [SDK client](/kit/reference/client). Assignable, to swap in a client you built yourself.</ResponseField>
<ResponseField name="Quickbutik.cart" type="CartStore | null">The shared [cart store](/kit/reference/cart): `subscribe`, `getSnapshot`, `load`, `refresh`, `add`, `updateItem`, `removeItem`, `clear`, `buyNow`, `hydrate`. Every cart element updates from it.</ResponseField>
<ResponseField name="Quickbutik.clearCart()" type="Promise<void>">Empty the basket. Every `qb-cart` and `qb-cart-count` updates.</ResponseField>
<ResponseField name="Quickbutik.configure(options)" type="ShopContextValue">Same as [`configure()`](/kit/reference/elements#configure). Call it once.</ResponseField>

### Currency

<ResponseField name="Quickbutik.currency" type="string | null">The currency the page browses in: the shopper's remembered choice, else `data-currency`, else `null` (the shop's own).</ResponseField>
<ResponseField name="Quickbutik.setCurrency(code)" type="Promise<void>">Reprice the page: every `qb-product`, `qb-product-list` and cart element reloads in the new currency, the same cart re-read. Remembered for the next visit. `null` returns to the default. The markup twin is `<qb-currency-select>`.</ResponseField>
<ResponseField name="Quickbutik.onCurrencyChange(listener)" type="() => void">Be told when the page's currency changes. Returns an unsubscribe.</ResponseField>
<ResponseField name="Quickbutik.describeCurrency(shop, code)" type="CurrencyInfo">Resolve a choice against what the shop offers: `{ currency, baseCurrency, mode, rate, chargeCurrency, currencies }`. See [Currencies](/kit/concepts/currencies).</ResponseField>

### Consent and analytics

<ResponseField name="Quickbutik.consent" type="ConsentStore">The page's consent decision: `allows(category)`, `acceptAll()`, `rejectAll()`, `save(choice)`, `reset()`, `openSettings()`, `subscribe()`, `getSnapshot()`. Created with its defaults the first time it is read; the `data-consent-*` attributes configure it.</ResponseField>
<ResponseField name="Quickbutik.analytics" type="Analytics | null">The ambient shop's analytics hub, or `null` when analytics is off or nothing is configured.</ResponseField>
<ResponseField name="Quickbutik.track(event)" type="void">Send a commerce event of your own through the hub, for example a `view_item_list` on a category page or a `page_view` on a soft navigation. A no-op with analytics off.</ResponseField>
<ResponseField name="Quickbutik.trackPurchase(purchase, { storeId?, orderNumber? })" type="boolean">Report an order with data of your own (a legacy-checkout shop, a thank-you page that is not `<qb-order-confirmation>`). Deduplicated per order in this browser; `false` when it was already reported.</ResponseField>

`ConsentStore`, `Analytics`, `readConsentCookie`, `appendCheckoutHandoffParams`, `googleTagDestination` and `metaPixelDestination` are on the global too, for a page wiring its own. See [Utilities](/kit/reference/utilities#consent-and-analytics).

### Setup

<ResponseField name="Quickbutik.defineElements(options?)" type="{ prefix, tags }">Register the elements yourself, after `data-no-auto-define`.</ResponseField>

<ResponseField name="Quickbutik.elementConstructors" type="Record<string, CustomElementConstructor>" />

<ResponseField name="Quickbutik.defined" type="{ prefix, tags } | null">What was registered on load.</ResponseField>
<ResponseField name="Quickbutik.error" type="Error | null">A malformed `data-publishable-key`, if there was one. Also logged to the console.</ResponseField>
<ResponseField name="Quickbutik.version" type="string">The bundle's version, for example `"1.8.0"`.</ResponseField>

### SDK

| Member | |
| - | - |
| `createShopkitClient`, `ShopkitClient` | Build a client of your own |
| `ShopkitError`, `ShopkitApiError`, `ShopkitConfigError`, `ShopkitNetworkError`, `ShopkitScopeError` | The [error classes](/kit/reference/errors) |
| `storefrontRefusal`, `isStorefrontId`, `isStorefrontSurface`, `STOREFRONT_SURFACES`, `DEFAULT_STOREFRONT_SURFACE` | Campaign storefronts |
| `SUCCESS_MODES` | `["redirect", "inline"]` |
| `describeCurrency` | Resolve a currency choice against the shop's offer |
| `CartStore`, `ProductController`, `AsyncResource` | The framework-free state classes |

### Helpers

| Member | |
| - | - |
| `formatMoney(amount, currency, { locale })` | `Quickbutik.formatMoney(129900, "SEK", { locale: "sv-SE" })` gives `"1 299,00 kr"` |
| `formatMoneyRange(min, max, currency)` | |
| `buildSeo(options)`, `toNextMetadata(tags)` | [SEO builder](/kit/reference/utilities) |
| `resolveProductImageUrl`, `resolveCategoryImageUrl`, `buildImageSrcSet`, `applyImageTransform`, `pickProductImage`, `productImages` | [Image helpers](/kit/reference/utilities) |

### Recipes

```html theme={null}
<script type="module">
  // A search box driving the SDK directly
  document.querySelector("#search").addEventListener("input", async (event) => {
    const { data } = await Quickbutik.client.products.search({ search: event.target.value, limit: 10 })
    render(data)
  })

  // A badge of your own on the shared store
  Quickbutik.cart.subscribe(() => {
    document.querySelector("#badge").textContent = Quickbutik.cart.getSnapshot().itemCount
  })
  Quickbutik.cart.load()

  // React to the elements
  addEventListener("qb:added-to-cart", (event) => openDrawer(event.detail.cart))
</script>
```

Use `Quickbutik.cart` rather than a second client for the cart, or your code and the elements disagree until the next reload.

## Content Security Policy

The bundle evaluates no strings (no `eval`, no `new Function`, no dynamic imports). With analytics on (the default) it loads the merchant's own vendors once the shopper agrees, so the policy must allow them too:

```
script-src  'self' https://cdn.jsdelivr.net https://www.googletagmanager.com https://connect.facebook.net
connect-src https://commerce.quickbutik.com https://www.google-analytics.com https://*.analytics.google.com https://www.facebook.com
img-src     https://cdn.quickbutik.com https://www.facebook.com https://www.google-analytics.com
frame-src   https://pay.quickbutik.com
```

`script-src` names wherever you serve the file from. With `data-analytics="false"` (or `data-consent="false"`) the vendor hosts can go, and the bundle loads nothing else on its own. `frame-src` is only needed for the inline checkout, `<qb-checkout>`. Every tag the analytics layer injects is `async` and carries `data-qb-analytics`. The JSON-LD that `<qb-seo>` writes is data, not code, and no directive governs it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.