Basket pages and placement
Two things to settle before anything renders: which URLs are your basket and confirmation pages, and where on those pages the gift block should go.
Registering your basket and confirmation pages
Section titled “Registering your basket and confirmation pages”The widget does not guess what a basket page is. The server derives a page type from the URL using your stored patterns, and returns it in the config response. Set them in the dashboard → Install → Page detection & eligibility, or tell us and we will set them.
Patterns are matched against the pathname only. Query string and hash are stripped before matching, so never put ? in a pattern.
| Form | Meaning |
|---|---|
*/cart | Glob, anchored to the whole path. * matches anything. Matches /cart, /en-gb/cart, /checkout/cart. Does not match /collections/cart-accessories. |
*/cart/* | Sub-paths and trailing slash: /cart/, /cart/12345. |
/cart | No * at all = bare substring match. Matches any path containing /cart, including /collections/cart-accessories. Avoid. |
!*/products/* | Exclusion. If it matches, that page type is refused whatever else matched. Order-independent; an exclusion always wins. |
Defaults for a new store:
basket: */cart, */cart/*, */cart.php, */basket, */basket/*, */bag, */bag/*, !*/collections/*, !*/products/*, !*/blogs/*, !*/pages/*
confirmation: */order-confirmation, */order-confirmation/*, */checkout/success, */checkout/success/*, */thank-you, */thank-you/*, !*/collections/*, !*/products/*, !*/blogs/*, !*/pages/*Notes for custom platforms:
- Confirmation is tested before basket, so a path that matches both derives
confirmation. - Localised routes need their own entries:
*/warenkorb,*/panier,*/カート. Keep the leading*/, because a bare pattern is a substring match. - If your confirmation page lives on a different subdomain from your basket (
checkout.example.com), that works: the selection rides a first-party cookie on your registrable domain, so it survives the hop. But the confirmation subdomain must be covered by your registered domains (subdomains of a registered domain are covered automatically). - Anything that matches neither type is
(other): the widget renders nothing there. That is the expected state for 99% of a site-wide install.
Where the widget renders
Section titled “Where the widget renders”Resolution order, per page:
- Your explicit selector, if one is configured (default:
[data-gwp-slot]). The widget appends its shadow host inside the first match. An explicit selector is treated as your instruction and is used even if the element is currently empty or unlaid-out. - Auto-injection at a fallback position, if no selector is configured or the selector matches nothing. It creates its own
<div data-gwp-fallback>at one of:after_summary: after the first visible order-summary/totals element ([data-order-summary], or a class/id containingsummaryortotals).before_checkout: immediately before your checkout CTA (a link/button matchingcheckout, or text like "checkout", "pay now", "place order"). Degrades toafter_summaryon the confirmation page.top_of_basket: top ofmain/[role=main]/#main/#content.
- Nothing. If no safe anchor is found, the widget fails closed and retries as the DOM changes.
Recommended for custom platforms: put an explicit mount point in both templates and forget about the heuristics.
<!-- basket template, wherever the gift block should appear --><div data-gwp-slot></div>
<!-- order-confirmation template --><div data-gwp-slot></div>Both surfaces are configured independently (basket selector + position, confirmation selector + position), so you can use different hooks per template. The container we inject is removed again at unmount; we never leave orphaned nodes behind.
Give the confirmation page a mount point too. The redemption card ("Your free gift is ready" + the claim button) renders there, and an explicit slot is the only way to be sure it lands where you want it. The fallback anchors are heuristics and can fail closed on an unusual confirmation template. If the widget cannot resolve a slot there, the claim moment has to be delivered by your own order-confirmation email instead (see the claim moment).
If your basket is a slide-out drawer
Section titled “If your basket is a slide-out drawer”A drawer opens with no URL change, so it cannot be derived from the URL. It is supported, off by default, and configured with two CSS selectors we store for you:
root: the element that wraps the whole drawer, used asdocument.querySelector(root).openWhen: evaluated asroot.matches(openWhen)to tell open from closed (e.g.[open],.active,[aria-expanded="true"]).
Verify both in your own console before sending them to us: document.querySelector(ROOT) returns exactly one element, and document.querySelector(ROOT).matches(OPEN_WHEN) is true while open and false while closed. Do not use a geometry/visibility test. Some drawers have no layout box in either state.
Drawer caveats: the tag must be site-wide; auto-injection inside the drawer is fixed at the top of the scrollable items region and the fallbackPosition field is inert for that surface (if you need the block somewhere specific, give us a selector authored for the drawer and we render inside that element verbatim); frequency caps are counted separately for the drawer and the cart page; and on a URL that is a real basket page, the cart page always wins. The drawer never mounts a second copy.
Where next
Section titled “Where next”- Cart adapters — how the widget reads your cart total and currency on the pages you have just registered.
- Verify and troubleshoot — check that your patterns and slot resolve in a real browser.