Headless and single-page storefronts
Use ShopAssist on Next.js, Hydrogen, Nuxt and other SPAs: route changes, the JavaScript API, strict CSP, the SRI-pinned loader and identity.
Headless storefronts use the Generic connector for catalog and orders (or the Shopify app for a Shopify backend with a custom frontend) and embed the same widget.
Load the widget once
Add the loader once in your document shell, not inside a route component, so navigation doesn't reload it:
<script src="https://cdn.shopassist.coec.dk/widget/<version>/loader.js" async
integrity="sha384-<published hash>" crossorigin="anonymous"
data-shopassist-key="pk_live_YOUR_PUBLISHABLE_KEY" data-platform="generic"></script>The first pinned release is published with the widget's general availability; until then use the channel loader below. Your dashboard shows the current version and hash.
In Next.js, put the tag in the root layout's <body> as a plain <script async>; avoid wrappers that re-insert it on every route.
Tell the widget about route changes
The widget reads the page type and product from the embed on load. On client-side navigation, call setPageContext so recommendations and proactive messages match the current page:
ShopAssist.setPageContext({ type: "product", productId: "SHIRT-42", variantId: "SHIRT-42-BLUE", title: "Linen shirt" })
// on a category page
ShopAssist.setPageContext({ type: "category", categoryId: "summer" })Use your catalog ids (the same external_id values as in your feed or Push API). Calls made before the loader finished are queued if you use ShopAssistQueue:
;(window.ShopAssistQueue = window.ShopAssistQueue || []).push(["setPageContext", { type: "home" }])JavaScript API
Open the chat from your own button, react to events and pass the cart through your own code. See the JavaScript API, events and the cart handler.
Content Security Policy
Strict CSPs need four directives. The pinned loader with SRI keeps an end-to-end integrity chain:
script-src https://cdn.shopassist.coec.dk; connect-src https://api.shopassist.coec.dk https://cdn.shopassist.coec.dk; img-src https://cdn.shopassist.coec.dk <store media hosts> blob:; style-src https://cdn.shopassist.coec.dk| Directive | Sources | Why |
|---|---|---|
script-src | https://cdn.shopassist.coec.dk | The loader and the chat app. Shopify serves the loader from the theme asset host; the app still needs our CDN. |
connect-src | https://api.shopassist.coec.dk https://cdn.shopassist.coec.dk | The widget API, streamed replies (SSE over fetch), the analytics beacon, the signed manifest and language packs. |
img-src | https://cdn.shopassist.coec.dk <your store media hosts> blob: | Avatars and logos from our CDN, product and store images from your own media hosts, blob: for photo previews and attachments. |
style-src | https://cdn.shopassist.coec.dk | Only used when the browser cannot apply constructable stylesheets (older Safari). 'unsafe-inline' is never needed. |
trusted-types | shopassist | Only when you enforce require-trusted-types-for 'script'. |
Details: Content Security Policy.
Signed-in customers
Register an identity provider that asks your server for a token:
ShopAssist.identify(async ({ sessionId }) => {
const r = await fetch("/my-server/shopassist-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session_id: sessionId }),
})
return r.ok ? ((await r.json()).token ?? null) : null
})Call ShopAssist.identify(null) when the customer signs out. Server side: identity JWT.
Last updated on
Checkout and cart for Generic stores
Configure cart mode, the checkout URL template with items, code, conversation id and locale, and the JavaScript cart handler.
Verify your installation
A checklist to confirm the widget, catalog, AI and identity work on your storefront, and the most common failures with their fixes.