> For the complete documentation index, see [llms.txt](https://en-ascent.webvista.studio/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://en-ascent.webvista.studio/developer/custom-add-to-cart-and-cart-drawer.md).

# Custom Add to cart button

Use this guide when you create a custom Add to cart button, custom Liquid block, or custom JavaScript that needs to add a product to the cart and open Ascent's native cart drawer.

## Recommended method

The safest method is to reuse Ascent's built-in `<product-form>` element. It already handles loading states, errors, cart bubble updates, cart drawer updates, and opening the drawer.

```liquid
<script src="{{ 'product-form.js' | asset_url }}" defer="defer"></script>

<product-form class="product-form" data-section="{{ section.id }}">
  <div
    id="Product-Form-Error-Message-{{ section.id }}"
    class="alert-message alert-error mb-2"
    role="alert"
    aria-live="assertive"
    hidden
  ></div>

  {% form 'product', product, class: 'form', novalidate: 'novalidate', data-type: 'add-to-cart-form' %}
    <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
    <input type="hidden" name="quantity" value="1">

    <button type="submit" name="add" class="button button--primary">
      Add to cart
    </button>
  {% endform %}
</product-form>
```

For this method to open the drawer, the store cart type should be set to drawer and the cart drawer should be present in the theme.

## Custom JavaScript method

If you need to add to cart from a fully custom script, send the required sections with the Ajax cart request, update the returned sections, then open the drawer.

```html
<button type="button" data-custom-add-to-cart data-variant-id="1234567890">
  Add to cart
</button>

<script>
  document.querySelector("[data-custom-add-to-cart]")?.addEventListener("click", async (event) => {
    const button = event.currentTarget;
    const cartDrawer = document.getElementById("Cart-Drawer");
    const sectionsToRender = [
      {
        id: "Cart-Icon-Bubble",
        section: "cart-icon-bubble",
        selector: ".shopify-section"
      }
    ];

    if (cartDrawer && !cartDrawer.hasAttribute("data-status-silence")) {
      sectionsToRender.push({
        id: "Cart-Drawer",
        section: cartDrawer.dataset.section,
        selector: "#Cart-Drawer-Details"
      });
    }

    const formData = new FormData();
    formData.append("id", button.dataset.variantId);
    formData.append("quantity", "1");
    formData.append("sections", sectionsToRender.map((section) => section.section).join(","));
    formData.append("sections_url", window.location.pathname);

    const response = await fetch(window.routes.cart_add_url, {
      method: "POST",
      headers: {
        "X-Requested-With": "XMLHttpRequest"
      },
      body: formData
    });

    const cartData = await response.json();

    if (cartData.status) {
      console.error(cartData.description || cartData.message);
      return;
    }

    SectionDynamicUpdate.updateSections(sectionsToRender, cartData.sections);

    webvista.publish(PUB_SUB_EVENTS.cartUpdate, {
      source: "custom-add-to-cart",
      productVariantId: formData.get("id"),
      cartData
    });

    if (cartDrawer && !cartDrawer.hasAttribute("data-status-silence")) {
      cartDrawer.show(button);
    } else {
      window.location = window.routes.cart_url;
    }
  });
</script>
```

Replace `1234567890` with the variant ID you want to add.

## Notes

* Use a variant ID, not a product ID.
* Include `sections_url` so Shopify returns section HTML for the current page.
* Do not open the drawer on the cart page; Ascent silences the drawer there with `data-status-silence`.
* If you only need a normal product add-to-cart button, prefer the built-in `<product-form>` method.
