Next Commerce
Upsells

Accept Button

data-next-action="accept-upsell" activates a button-only enhancer that submits a post-purchase upsell when clicked. It does not render anything — it only manages the button's enabled/disabled state and fires a single API call.

The button is enabled when both of these are true:

  • orderStore.canAddUpsells() returns true (order loaded, supports upsells, no submission in flight)
  • A package can be resolved (from a direct attribute or a linked selector)

Direct Package

Set data-next-package-id to a fixed package. Use this when the offer never varies.

<button
  data-next-action="accept-upsell"
  data-next-package-id="42"
  data-next-quantity="1"
  data-next-url="/upsell-2">
  Yes, add to my order
</button>

The optional data-next-url overrides the <meta name="next-upsell-accept-url"> redirect for this specific button.


Selector-Driven (Variant Choice)

Pair the button with a PackageSelectorEnhancer running in upsell context. The selector handles the visual choice; the button reads the current selection and submits it.

<div
  data-next-package-selector
  data-next-selector-id="upsell-pkg"
  data-next-upsell-context>

  <div data-next-selector-card data-next-package-id="42" data-next-selected="true">
    1 bottle — $29
  </div>
  <div data-next-selector-card data-next-package-id="43">
    3 bottles — $69
  </div>
</div>

<button
  data-next-action="accept-upsell"
  data-next-selector-id="upsell-pkg"
  data-next-url="/upsell-2">
  Add to my order
</button>

The data-next-selector-id value must match between the selector container and the button.

data-next-upsell-context on the selector is required. It puts the selector into select mode and disables all cart writes. Without it, clicking a card would write to the cart, which is wrong on a post-purchase page.


Bundle-Driven

Pair the button with a BundleSelectorEnhancer to submit a multi-package bundle as the upsell. Use data-next-upsell-action-for (not data-next-selector-id) to link them.

<div
  data-next-bundle-selector
  data-next-selector-id="upsell-bundle"
  data-next-upsell-context>

  <div data-next-bundle-card
       data-next-bundle-id="duo"
       data-next-bundle-items='[{"packageId":42,"quantity":1},{"packageId":43,"quantity":1}]'
       data-next-selected="true">
    Duo Pack
  </div>
</div>

<button
  data-next-action="accept-upsell"
  data-next-upsell-action-for="upsell-bundle">
  Add bundle to my order
</button>

When linked to a bundle, the button submits all items in the selected bundle as a single addUpsell call. One upsell:accepted event is emitted per package.


Programmatic Submission

The enhancer exposes triggerAcceptUpsell() on the bound element. This goes through the same path as a real click — duplicate detection, redirect, and all.

const button = document.querySelector('[data-next-action="accept-upsell"]');
await button.triggerAcceptUpsell();

triggerAcceptUpsell() is only available after the SDK has initialized the button. Call it after next:initialized or in a nextReady callback.


Duplicate Detection

If the customer has already accepted the same package in this session, clicking the button shows a confirmation dialog before re-submitting. "Yes, add again" submits a second time; "Skip to next" navigates to the decline URL without submitting.

Duplicates are tracked in orderStore.completedUpsells and orderStore.upsellJourney. The state persists across pages (sessionStorage), so the dialog will appear if the customer hits Back and clicks the same button again.


bfcache Recovery

When the customer navigates away and presses the browser Back button, the page may be restored from the back/forward cache. In that case the loading overlay from the previous click is cleared and the button is re-enabled automatically — the customer is never stuck on a "loading" state from a stale click.


Conflicts

  • Do not put data-next-action="add-to-cart" on the same button. add-to-cart writes to the cart store; accept-upsell writes to the order API. They are independent stores and writing to both for the same item produces duplicate records.
  • Do not pair the accept button with a CartToggleEnhancer for the same package on a post-purchase page — same conflict as above.
  • For variant choice on an upsell page, always use a PackageSelectorEnhancer with data-next-upsell-context. Do not use a regular package selector — it will write to the cart.

Next Steps

On this page