Next Commerce
Upsells

Selectors

There are four ways to let the customer pick which package gets submitted as the upsell. Three live inside an offer container; one links to an external selector enhancer.

PatternWhere the options liveUse when
Built-in option cardsInside the offer containerThe offer card and the chooser are one widget
Built-in dropdownInside the offer containerYou want a compact <select> instead of cards
Linked PackageSelectorOutside the offer containerThe selector and the accept button are separate elements on the page
Linked BundleSelectorOutside the offer containerThe upsell is a multi-package bundle

Built-in Option Cards

Use data-next-upsell-selector on a container, then data-next-upsell-option on each card. Each card needs its own data-next-package-id.

<div data-next-upsell-selector data-next-selector-id="protection">
  <h3>Choose Your Protection Plan</h3>

  <div data-next-upsell-option data-next-package-id="50">
    <h4>Basic</h4>
    <p>1 year coverage — $14.99</p>
  </div>

  <div data-next-upsell-option data-next-package-id="51" data-next-selected="true">
    <h4>Premium</h4>
    <p>2 year coverage + accidental damage — $24.99</p>
  </div>

  <div data-next-upsell-option data-next-package-id="52">
    <h4>Ultimate</h4>
    <p>3 year coverage + everything — $39.99</p>
  </div>

  <button data-next-upsell-action="add">Add selected protection</button>
  <button data-next-upsell-action="skip">No protection, thanks</button>
</div>

The selected option gets the next-selected class and data-next-selected="true". Pre-select a card by setting data-next-selected="true" on it in the markup.

The enhancer emits upsell:option-selected with { selectorId, packageId } whenever the selection changes.


Built-in Dropdown

Use a native <select> with data-next-upsell-select="<selectorId>". Each <option> value is a package ID.

<div data-next-upsell-selector data-next-selector-id="training">
  <h3>Add a training course?</h3>

  <select data-next-upsell-select="training">
    <option value="">Choose a course...</option>
    <option value="2">Beginner — $29.99</option>
    <option value="3" selected>Advanced — $49.99</option>
    <option value="4">Master — $79.99</option>
  </select>

  <button data-next-upsell-action="add">Add course to order</button>
  <button data-next-upsell-action="skip">No thanks</button>
</div>

The default <option> (with selected) is picked up on init. Selecting an empty value clears the selection and disables submission.

You can also place a bare <select> element with data-next-upsell-select and no wrapping container — it activates on its own.


Linked PackageSelector

Run a PackageSelectorEnhancer outside the offer container, then point an Accept Button at it via a shared data-next-selector-id.

<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">
  Add to my order
</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.

You can also link a linked package selector to an offer container by setting data-next-package-selector-id="<selector-id>" on the container, but the accept-button pattern above is simpler.


Linked BundleSelector

Run a BundleSelectorEnhancer outside the offer, then link it to an Accept Button using data-next-upsell-action-for (note: this is a different attribute from data-next-selector-id).

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

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

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

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

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

You can also link a bundle selector to an offer container by setting data-next-bundle-selector-id="<selector-id>" on the container, or by nesting the bundle selector as a child of the container — the offer container auto-detects child bundle selectors.


Cross-Container Sync

When two containers share the same data-next-selector-id value, selecting an option in one syncs the selection in the other. This is useful for sticky headers, side-by-side comparison views, or duplicating the offer above and below the fold.

<!-- Sticky header version -->
<div class="sticky-header" data-next-upsell-selector data-next-selector-id="protection">
  <!-- compact options -->
</div>

<!-- Full version below the fold -->
<div data-next-upsell="offer" data-next-selector-id="protection">
  <!-- full option cards -->
</div>

Next Steps

On this page