Shopify
Add Rybbit analytics to your Shopify store
On Shopify the snippet goes into your theme's theme.liquid layout, which wraps every storefront page. Checkout runs on Shopify's own sandboxed pages, so purchases are tracked separately through a custom web pixel.
Get your tracking snippet
In your Rybbit dashboard, open Site Settings → Tracking Script and copy your snippet. It looks like this:
<script src="https://app.rybbit.io/api/script.js?siteId=YOUR_SITE_ID" defer></script>YOUR_SITE_ID is the numeric ID of your site. If you self-host Rybbit, app.rybbit.io is the domain of your own instance.
Add the snippet to Shopify
- In Shopify admin, go to Online Store > Themes.
- On your current theme, click the ... button next to Customize and choose Edit code.
- Open
theme.liquidin the Layout folder. - Paste the snippet just before the closing
</head>tag and click Save.
Theme updates can overwrite theme.liquid. Shopify has no child-theme system, so after updating a purchased theme, re-add the snippet.
Verify installation
Open your live site in a new tab and click through a few pages. Within a few seconds the pageviews appear in the Rybbit dashboard.
If nothing shows up:
- View the page source and search for
script.js?siteId=to confirm the snippet is on the page. - Open the browser Network tab and check that
script.jsreturns200and thatPOSTrequests go to/api/track. - Disable ad blockers, or set up a proxy so the script loads from your own domain.
- See the script troubleshooting guide for other common causes.
Track custom events
Storefront templates can call window.rybbit.event() directly. Wrap the calls in a DOMContentLoaded listener because the tracking script is loaded with defer, and divide Liquid prices by 100 because they are in cents.
Back up your theme before editing code. Liquid object names such as product.selected_or_first_available_variant and the form selectors below vary between themes, so adjust them to match yours.
View product
Add to your main product template (sections/main-product.liquid or templates/product.liquid, depending on the theme):
<script>
document.addEventListener('DOMContentLoaded', function () {
var variant = {{ product.selected_or_first_available_variant | json }};
window.rybbit.event('view_item', {
item_id: variant.sku || variant.id,
item_name: {{ product.title | json }},
item_variant: variant.title === 'Default Title' ? null : variant.title,
price: variant.price / 100,
currency: {{ cart.currency.iso_code | json }}
});
});
</script>Add to cart
In the same template, listen on the product form. Themes with an AJAX cart drawer usually fire their own JavaScript event after an item is added; hooking that is more reliable than the form submit.
<script>
document.addEventListener('DOMContentLoaded', function () {
var form = document.querySelector('form[action="/cart/add"]');
if (!form) return;
form.addEventListener('submit', function () {
var variant = {{ product.selected_or_first_available_variant | json }};
var qty = form.querySelector('[name="quantity"]');
window.rybbit.event('add_to_cart', {
item_id: variant.sku || variant.id,
item_name: {{ product.title | json }},
price: variant.price / 100,
currency: {{ cart.currency.iso_code | json }},
quantity: qty && parseInt(qty.value, 10) > 0 ? parseInt(qty.value, 10) : 1
});
});
});
</script>Begin checkout
Add to your cart template (sections/main-cart-items.liquid or templates/cart.liquid) to track clicks on the checkout button:
<script>
document.addEventListener('DOMContentLoaded', function () {
var button = document.querySelector('[name="checkout"]');
if (!button) return;
button.addEventListener('click', function () {
var cart = {{ cart | json }};
window.rybbit.event('begin_checkout', {
currency: cart.currency,
value: cart.total_price / 100,
items: cart.items.map(function (item) {
return { item_id: item.sku || item.variant_id, quantity: item.quantity, price: item.final_price / 100 };
})
});
});
});
</script>Event properties are limited to 2048 characters of serialised JSON and larger payloads are rejected, not truncated. Keep items compact, or send only the totals for large carts.
Purchase
Checkout and the order status page do not load your theme, and the old Order status page > Additional scripts field is deprecated: view-only since August 28, 2025, with non-Plus stores required to migrate by August 26, 2026. Track purchases with a custom web pixel from Checkout Extensibility instead.
Pixels run in a sandbox with no access to window.rybbit, so the pixel posts the event straight to Rybbit's track endpoint.
- In Shopify admin, go to Settings > Customer events.
- Click Add custom pixel, name it
Rybbit, and click Add pixel. - Paste the code below, replace
YOUR_SITE_ID, then Save and Connect the pixel.
analytics.subscribe("checkout_completed", (event) => {
const checkout = event.data.checkout;
const items = (checkout.lineItems || []).map((li) => ({
id: li.variant?.sku || li.variant?.id,
qty: li.quantity,
price: li.variant?.price?.amount,
}));
fetch("https://app.rybbit.io/api/track", {
method: "POST",
headers: { "Content-Type": "application/json" },
keepalive: true,
body: JSON.stringify({
type: "custom_event",
site_id: "YOUR_SITE_ID",
event_name: "purchase",
// properties must be a JSON string of at most 2048 characters
properties: JSON.stringify({
transaction_id: checkout.order?.id || checkout.token,
value: checkout.totalPrice?.amount,
currency: checkout.currencyCode,
items,
}),
}),
});
});checkout_completed fires once per placed order. The same Customer Events API also exposes product_added_to_cart and checkout_started, so you can move add-to-cart and begin-checkout into the pixel too if theme code is unreliable on your store.
Next steps
- Track custom events such as signups, purchases and button clicks.
- Identify users to connect sessions to accounts.
- Proxy the script through your own domain to bypass ad blockers.
- Script attributes let you skip or mask URLs and tag events.