Milly Software
InsightsEngagementSticky Add-to-Cart Bar in Chat: PDP-Aware Conversion on Every Format
Engagement··6 min read

Sticky Add-to-Cart Bar in Chat: PDP-Aware Conversion on Every Format

A sticky in-chat ATC bar that follows the visitor across the conversation when they're on a product page — JSON-LD-driven detection, full variant grid with OOS handling, native + headless cart support.

V
Viet Le
co-founder · Milly Software

In-chat add-to-cart on a product card works when the AI just recommended that product. But what about when the visitor is already on the PDP, opens the chat to ask a question, and wants to buy the product they were already looking at? The recommendation card might never appear in the conversation. The visitor has the product on screen but no obvious way to add it without leaving chat.

The sticky ATC bar fixes that. When the visitor is on a product page, a persistent bar pins to the top of the chat surface with the product image, name, price, variant picker, and an Add to Cart button. It stays visible across the entire conversation, regardless of how the chat scrolls or what the AI is currently saying.

How the widget knows it's on a PDP

The bar only renders when the page is a product detail page. Detection runs a three-source cascade:

  1. JSON-LD Product schema — the most reliable source. Shopify themes ship Product schema in <script type="application/ld+json">on every PDP. The widget parses name, offers, and per-variant availability from this single block.
  2. Open Graph tags — fallback when JSON-LD isn't present. Reads og:type="product" plus og:title / og:description / product:price:amount.
  3. ShopifyAnalytics.meta.product — Shopify-specific window object that's present on most theme installs as a final fallback.

The first source that returns a product wins. The bar renders if and only if a product was detected; on collection pages, the homepage, the cart page, anywhere that isn't a PDP, the bar stays hidden.

The bar layout

The bar is intentionally simple. Left to right: product thumbnail (from the Open Graph image or the JSON-LD image field), product name (truncated if long), price (from offers.price), and an Add to Cart button.

The button has four states the user can see:

  • Add to Cart — default state when the product is single-variant or has only the Default Title variant. One click adds.
  • Select Option — multi-variant products with real options. Clicking opens the variant grid inline.
  • Adding... — request in flight (briefly visible).
  • Added ✓ — success state, persistent until the visitor changes pages or the conversation resets.

Error states (cart API failure, headless callback returning failure) flip the button to a red error state instead of the green success — the visitor sees the failure rather than a false confirmation.

Variant picker mirrors the PDP

For multi-variant products, the picker behaves the way a well-designed PDP picker does:

  • All variants render in the grid, including out-of-stock variants. The visitor sees the full color or size landscape — they don't assume the product is unavailable when one variant happens to be sold out.
  • OOS variants are disabled with a strikethrough. Visually present but unclickable. Mirrors the standard ecommerce pattern across major platforms.
  • The first available variant is selected by default, so the "just click Add" path works without the visitor explicitly picking.
  • Variant availability comes from JSON-LD — each offer entry has an availability field; the widget parses "https://schema.org/InStock" vs other states and disables OOS rows accordingly.

The picker matches the behavior of the in-chat ATC button on product cards — same component, same rules — so the visitor sees consistent picker UX whether they got to the Add action through a chat recommendation or through the sticky PDP bar.

Across all four widget formats

The chat surface has four formats: chat bubble, search bar, slideout panel, smart banner. The sticky ATC bar renders in all four. The implementation lives in a shared StickyATCBar component that's mounted at the top of the message area regardless of format.

That coverage matters because the format choice is independent of the visitor's page context. A merchant might use chat bubble on the homepage and search bar on collection pages and slideout on PDPs. Whichever format the visitor encounters, if they're on a PDP and they open the chat surface, they see the bar. The merchant doesn't have to think about which format gets ATC — they all do.

Native + headless cart support

The bar uses the same cart-mode contract as in-chat ATC on product cards:

  • Native (cart_mode: ajax) — POSTs to /cart/add.js with the _millyChatAssisted attribution flag, dispatches cart:build + cart:updated DOM events for theme-side cart UIs to refresh.
  • Headless (cart_mode: callback) — emits an add_to_cart callback via window.MillyChat.on() with the variant data, and waits for the host page to call MillyChat.addToCartResult() before flipping to the success or error state.

Same contract, same data shape, same callback API. The sticky bar isn't a different cart path — it's a different entry point into the same path. Native and headless merchants get identical PDP-aware ATC behavior; the merchant just configures cart-mode once.

Try Milly Chat

Want to see how this fits your store?
We'll set up a working session.

Get it on ShopifyTalk to sales →