> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add-to-cart interception

> Why Aftersell takes over add-to-cart, how to tell whether a form is intercepted, and every way to opt a form out.

When a shopper clicks **Add to cart**, Aftersell usually handles the add itself instead of letting the theme do it. This page explains why, what it means for scripts you've added, and how to turn it off for one form or for all of them.

Most stores never need to change any of this. Read on if a script of yours stopped firing on add-to-cart, or if an add-to-cart button behaves oddly.

## What interception does

Aftersell listens for the add-to-cart submit before the theme does. When it recognizes one, it:

1. Stops the event, so nothing else on the page handles that click.
2. Sends the add to Shopify itself.
3. Opens the Aftersell Cart drawer.

Step 1 is the important one, and it's the reason this page exists.

## Why it exists

Without it, both carts respond to the same click. The theme adds the item and opens its own drawer, Aftersell adds it and opens ours, and the shopper sees two carts, often with the item added twice.

Stopping the event is the simplest way to guarantee one add and one cart.

## What it costs

Stopping the event stops it for **everyone**, not just the theme. Any other code listening to that same add-to-cart stops running: your analytics, a tracking pixel, a subscription or bundle app, a script you added yourself.

It fails quietly. Nothing appears in the browser console, and the add itself still works, so the usual symptom is a number that's wrong rather than something visibly broken:

* `add_to_cart` events missing from GA4, Meta, or TikTok
* A subscription or bundle app that works on the product page but not through the cart
* Your own `addEventListener` on the form never firing

If any of that sounds familiar, this page is the cause and the fix is below.

## When Aftersell doesn't intercept

Interception isn't always on. Aftersell leaves add-to-cart alone when:

* **It recognizes your theme's cart.** On themes Aftersell knows how to work with, it makes the theme's own cart inert instead of blocking the event, then lets the theme perform the add normally. Your scripts run as they always did. See [Which themes](#which-themes-aftersell-recognizes) below.
* **The form doesn't add a line item.** A form with no variant `id` and no `items[]` is left alone.
* **You've opted out** using one of the methods below.

When Aftersell doesn't perform the add, it still watches for the cart request and opens the drawer when it sees one. See [Before you choose: what changes](#before-you-choose-what-changes).

## Which themes Aftersell recognizes

| Theme                                          |                                                                                             |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------- |
| **Dawn** and the rest of Shopify's free family | Craft, Colorblock, Crave, Origin, Publisher, Refresh, Ride, Sense, Spotlight, Studio, Taste |
| **Horizon**                                    | Shopify's current default theme                                                             |
| **Impulse**                                    |                                                                                             |

Aftersell matches on **how a theme is built**, not on its name, so a custom theme forked from any of these is normally recognized too, including private builds Aftersell has never seen.

<Note>
  The reverse also happens: a heavily customized build can drift far enough from its parent that Aftersell no longer recognizes it, even though the theme is still called "Dawn". Being on this list makes recognition likely, not certain.
</Note>

## Your options

Choose the narrowest one that solves your problem. Each row gives up more than the one above it.

| Option                                                               | Scope                     | Aftersell still opens the drawer |
| -------------------------------------------------------------------- | ------------------------- | -------------------------------- |
| [`registerSkipAddToCartRule`](#per-form-a-rule-in-code)              | The forms your rule picks | Yes, from the cart request       |
| [`aftersell-cart-skip-atc`](#per-form-a-class-in-your-theme)         | One form or button        | Yes, from the cart request       |
| [`skip_add_to_cart_interceptor`](#whole-store-turn-interception-off) | Every form on the store   | Yes, from the cart request       |

### Before you choose: what changes

Opting out hands the add back to your theme, which raises two questions worth answering before you pick a row: whether your cart still opens, and whether the theme's cart turns up next to it.

#### Will your cart still open?

Usually yes, with no work on your part. Whoever performs the add, Aftersell watches for the request going to Shopify and opens the drawer when it sees one, following your normal **Open cart when an item is added** setting. You don't have to call anything yourself.

Three situations break that, and all three have a fix:

**The add goes somewhere other than Shopify's cart endpoints.** Aftersell watches `/cart/add`, `/cart/change`, `/cart/update` and `/cart/clear` on your own domain. An app that adds through its own endpoint and syncs the cart afterwards isn't visible to this. Open the cart yourself once that app's add finishes:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.actions.refresh().then(() => {
  window.aftersell.cart.actions.open();
});
```

**More than about three seconds pass between the click and the request.** Aftersell treats an add that closely follows a real click or keypress as shopper-driven. Beyond that window it's considered a background add, which doesn't open the drawer unless you opt in:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.configure({ open_on_background_add: true });
```

**Your cart setting says not to.** If **Open cart when an item is added** is off, or you've set `open_on_add_to_cart: 'never'`, nothing opens the drawer. That's working as configured.

#### Will the theme's cart open too?

This is the risk the opt-outs carry, and the answer depends on your theme.

Making the theme's cart inert is **separate from interception** and happens at page load either way, so none of the opt-outs here switch it back on. On a theme from the [recognized list](#which-themes-aftersell-recognizes), the theme's own cart stays quiet and the shopper sees one cart, yours.

On a theme Aftersell doesn't recognize, there's nothing holding the theme's cart back. Opting out means the theme handles the add exactly as it always did, including opening its own drawer or redirecting to `/cart`, while Aftersell opens its drawer from the request it saw. That's two carts, and it's the reason interception exists in the first place.

If that happens, you have three choices: leave interception on for that form, use a narrower opt-out that doesn't cover the forms causing it, or stop the theme's own cart yourself in your theme code.

<Tip>
  Turn an opt-out on in a test or unpublished theme first. If the theme's own cart appears where it didn't before, your theme isn't one Aftersell recognizes, and you'll want to keep interception on for those forms.
</Tip>

<Note>
  This applies to add-to-cart only. Making the **cart icon** bypass Aftersell with the `aftersell-cart-wont-open-cart` class is different: a cart-icon click sends no request, so there's nothing for Aftersell to watch, and the drawer will not open. See below.
</Note>

### Per form: a rule in code

The preferred option. Register a rule that returns `true` for the forms you want left alone:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerSkipAddToCartRule((form) =>
  form.hasAttribute('data-skip-aftersell')
);
```

Put it in **Cart settings → Custom script → Initialization**. Rules are additive: yours runs alongside any others, and any rule returning `true` skips that form. Full details in [Hooks](/aftersell/cart/sdk-hooks#registerskipaddtocartrule).

### Per form: a class in your theme

If you'd rather not write a rule, add the `aftersell-cart-skip-atc` class in your theme:

```html theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
<form action="/cart/add" method="post" class="aftersell-cart-skip-atc">
```

<Note>
  For a form submit, the class must be on the **form element itself**. A parent `div` won't work. For a button that adds to the cart without a form submit, the class can be on the button or any element around it.
</Note>

### Whole store: turn interception off

The blunt option. Add-to-cart behaves exactly as your theme originally did, on every form:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.configure({ skip_add_to_cart_interceptor: true });
```

<Warning>
  This one is read **once, when the cart loads**. It only works from your cart's **Initialization** script. Setting it later, inside `ready()` or from an event handler, does nothing and fails silently.
</Warning>

Reach for this only when the per-form options don't fit, for example when the forms you need to exempt are created by another app and you can't identify them reliably.

## The cart icon is separate

The cart icon in your header is handled by its own interceptor, with its own opt-out. Turning off add-to-cart interception does not change what the cart icon does, and the reverse is also true.

Clicking the cart icon opens the Aftersell drawer instead of going to `/cart`. To leave one icon or button alone, add the `aftersell-cart-wont-open-cart` class to it, or to any element around it:

```html theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
<a href="/cart" class="aftersell-cart-wont-open-cart">Cart</a>
```

That control then does whatever your theme makes it do, usually going to the cart page. Aftersell is out of the picture entirely, so **the drawer will not open**. Unlike add-to-cart there's no request to watch for, so if you want your cart to open from that control you have to say so:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
document.querySelector('#my-cart-link').addEventListener('click', (event) => {
  event.preventDefault();
  window.aftersell.cart.actions.open();
});
```

The same silencing problem applies here: because Aftersell stops the click, your analytics and pixels don't see cart-icon clicks either. If that's all you need to fix, keep the drawer and stop the silencing:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.configure({ skip_open_cart_interceptor: true });
```

Your listeners run, the drawer still opens, and the click still doesn't navigate to `/cart`. Full details in [Configure](/aftersell/cart/sdk-configure#skip_open_cart_interceptor).

<Note>
  To change *which* elements open the cart rather than turning them off, use **Cart settings → Advanced → Cart icon selector** instead of editing your theme.
</Note>

## Where to go next

* **[Configure](/aftersell/cart/sdk-configure)**: every SDK option, including the ones referenced here.
* **[Hooks](/aftersell/cart/sdk-hooks)**: per-form and per-line control.
* **[Open the drawer from a page builder](/aftersell/cart/sdk-use-case-page-builder)**: for Replo, PageFly, GemPages, and custom buttons that add to the cart their own way.
