---
title: "Custom events"
description: "Define your own conversion events, publish them from theme code, and count them per variant."
lastUpdated: 2026-05-15
canonical: https://culsin.com/docs/simple-split-testing/custom-events/
source: https://culsin.com/docs/simple-split-testing/custom-events/
---
Standard events (page views, cart adds, checkout steps, orders) are tracked automatically by the web pixel. **Custom events** let you count anything else: a CTA click, a modal open, a form submission, scrolling past a fold.

---

## Define an event in the admin

1. In the dashboard, open **Custom events** from the nav
2. Click **Add event**
3. Pick a name. Letters, digits, dots, hyphens, underscores, 1 to 128 characters.
4. Save

Defining the event tells the pixel which event names to forward to the app. Events the pixel sees but that are not in the list are dropped, so you can call `Shopify.analytics.publish()` freely without ingesting noise.

---

## Publish an event from your theme

From any theme script (snippet, section, theme-app-block):

```html
<script>
  document.querySelector('#hero-cta')?.addEventListener('click', () => {
    Shopify.analytics.publish('checkout_cta_clicked', { source: 'hero' });
  });
</script>
```

The second argument is an optional `data` object. The pixel forwards it with the event.

---

## Naming rules

| Rule | Why |
|---|---|
| Letters, digits, underscores, hyphens, dots only (matches `^[a-zA-Z0-9_.\-]{1,128}$`) | The pixel filters anything else as malformed |
| Length between 1 and 128 characters | Fits a column header reasonably |
| Stable across deploys | Event counts attach to the name; renaming an event starts a new series |

The regex is intentionally loose so third-party event names like `Klaviyo.viewedProduct` or `add-to-wishlist` work without renaming. If a published event name does not match the rules, the pixel drops it silently.

---

## Using a custom event as a conversion goal

When you create or edit a test, you can pick any defined custom event as the primary KPI alongside standard ones (conversion rate, AOV, sessions, add-to-cart rate). The admin then tracks:

- Visitors per variant
- Conversions per variant (visitors who fired the goal at least once)
- Conversion rate
- A significance verdict against your confidence level

Conversions are counted **per visitor** (deduplicated). If the same visitor fires the goal three times in a session they are still counted once.

---

## Standard events you don't need to define

These are forwarded automatically when the pixel is active and never need to be defined as custom events:

| Event | Fires on |
|---|---|
| `page_viewed` | Every storefront page load |
| `collection_viewed` | Collection page |
| `product_viewed` | Product page |
| `cart_viewed` | Cart page |
| `search_submitted` | Storefront search |
| `product_added_to_cart` | Add-to-cart action |
| `product_removed_from_cart` | Remove-from-cart action |
| `checkout_started` | Checkout begins |
| `checkout_contact_info_submitted` | Contact step submitted |
| `checkout_address_info_submitted` | Address step submitted |
| `checkout_shipping_info_submitted` | Shipping step submitted |
| `payment_info_submitted` | Payment step submitted |
| `checkout_completed` | Order placed |

When you pick a conversion goal, this list (plus your custom events) is what appears in the dropdown.

---

## Consent gating

The app respects Shopify's Customer Privacy API. If a visitor has declined analytics processing or sale-of-data (CCPA), the embed does not bucket them, no cookies are set, and no events are forwarded. When the visitor later accepts consent, bucketing resumes on the next page load.

For devs: the embed reads `Shopify.customerPrivacy.analyticsProcessingAllowed()` and `saleOfDataAllowed()`, and subscribes to `visitorConsentCollected` to re-arm. On themes without consent management, both paths proceed and you remain responsible for your region's compliance.

---

## Debugging custom events

The same `?splt_debug=true` flag described in [storefront markup](/docs/simple-split-testing/storefront-markup#debugging) also enables console logging in the web pixel. If your event is being dropped and the console says nothing, double-check the name matches the rules above and is defined under **Custom events**.
