Analytics Architecture on Ecwid
Ecwid is an embeddable ecommerce widget that adds a store to any existing website. The store loads as a JavaScript application within the host page, rendering products, cart, and checkout without full page reloads. This SPA-like behavior has direct implications for analytics:
- No page loads on navigation -- browsing products, opening the cart, and entering checkout all happen within the Ecwid widget without triggering browser navigation events. Traditional page view tracking misses these transitions entirely
- The Ecwid JavaScript API exposes lifecycle callbacks (
Ecwid.OnPageLoaded,Ecwid.OnCartChanged,Ecwid.OnOrderPlaced) that serve as the primary hooks for analytics event tracking - Cross-domain tracking is required when the Ecwid store widget is embedded on a different domain than the Ecwid checkout, or when using Ecwid's Instant Site alongside an embedded store
- Plan restrictions affect analytics capabilities: Google Analytics integration is available on Venture plans and above, GTM on Business plans and above
Ecwid's built-in GA4 integration handles basic ecommerce events automatically when configured in the admin panel. For custom tracking beyond the built-in events, the JavaScript API is required.
Installing Tracking Scripts
Built-in Google Analytics Integration
Ecwid has a native GA4 integration that automatically sends enhanced ecommerce events:
- Navigate to Ecwid Admin > Marketing > Google Analytics
- Enter your GA4 Measurement ID (G-XXXXXX)
- Enable Enhanced Ecommerce tracking
This automatically tracks view_item, add_to_cart, begin_checkout, and purchase events within the Ecwid widget. No code is needed for these basic events.
GTM via Ecwid Admin (Business Plan+)
- Navigate to Ecwid Admin > Marketing > Google Tag Manager
- Enter your GTM Container ID (GTM-XXXXXX)
Ecwid injects the GTM container script into the store widget context. GTM tags fire within the widget's execution environment.
Manual Script Installation on Host Page
For analytics that cover both the host website and the embedded Ecwid store, add GTM to the host page directly:
<!-- Host page head - covers both host site and Ecwid widget -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
When GTM is on the host page, the Ecwid JavaScript API events push to the same window.dataLayer that GTM reads, so both host page and store events flow through a single container.
Tracking Store Events with the JavaScript API
The Ecwid JavaScript API provides callbacks for every store interaction. Use these to build a data layer that tracks the full ecommerce funnel:
Page/View Tracking
Ecwid.OnPageLoaded.add(function(page) {
window.dataLayer = window.dataLayer || [];
if (page.type === 'PRODUCT') {
dataLayer.push({ ecommerce: null });
dataLayer.push({
'event': 'view_item',
'ecommerce': {
'items': [{
'item_id': String(page.productId),
'item_name': page.name || ''
}]
}
});
} else if (page.type === 'CATEGORY') {
dataLayer.push({
'event': 'view_item_list',
'ecommerce': {
'item_list_name': page.name || 'Category'
}
});
} else if (page.type === 'CART') {
dataLayer.push({ 'event': 'view_cart' });
} else if (page.type === 'CHECKOUT') {
dataLayer.push({ 'event': 'begin_checkout' });
}
});
Cart Events
Ecwid.OnCartChanged.add(function(cart) {
window.dataLayer = window.dataLayer || [];
var items = cart.items.map(function(item) {
return {
'item_id': String(item.product.id),
'item_name': item.product.name,
'price': item.product.price,
'quantity': item.quantity
};
});
dataLayer.push({
'event': 'cart_updated',
'cart_item_count': cart.productsQuantity,
'cart_total': cart.total,
'ecommerce': { 'items': items }
});
});
Purchase Tracking
Ecwid.OnOrderPlaced.add(function(order) {
window.dataLayer = window.dataLayer || [];
dataLayer.push({ ecommerce: null });
var items = order.items.map(function(item) {
return {
'item_id': String(item.product.id),
'item_name': item.product.name,
'price': item.price,
'quantity': item.quantity,
'item_category': item.categoryName || ''
};
});
dataLayer.push({
'event': 'purchase',
'ecommerce': {
'transaction_id': String(order.orderNumber),
'value': order.total,
'currency': order.currency || 'USD',
'tax': order.tax || 0,
'shipping': order.shipping || 0,
'items': items
}
});
});
The OnOrderPlaced callback fires after payment is confirmed, making it reliable for conversion tracking. It only fires once per order.
Cross-Domain Tracking
Ecwid stores can run across multiple domains:
- Embedded store on your website (e.g.,
yoursite.com/store) - Ecwid Instant Site (e.g.,
store12345.ecwid.com) - Checkout may redirect to a different domain depending on payment configuration
Configure cross-domain tracking in GA4:
gtag('config', 'G-XXXXXX', {
'linker': {
'domains': ['yoursite.com', 'store12345.ecwid.com'],
'accept_incoming': true
}
});
In GTM, configure the GA4 Configuration tag's Cross Domain Measurement field with all domains where the Ecwid store or checkout appears.
If you use Ecwid's built-in GA4 integration alongside manual tracking on the host page, disable the Ecwid integration to avoid double-counting. Use one tracking method, not both.
Product Data Enrichment
The Ecwid.OnPageLoaded callback provides basic product data. For richer analytics, use the Ecwid REST API to fetch additional product details:
Ecwid.OnPageLoaded.add(function(page) {
if (page.type === 'PRODUCT') {
// Fetch full product data from Ecwid API
fetch('/api/v3/' + Ecwid.getOwnerId() + '/products/' + page.productId, {
headers: { 'Authorization': 'Bearer ' + ecwidPublicToken }
})
.then(function(res) { return res.json(); })
.then(function(product) {
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'event': 'view_item',
'ecommerce': {
'items': [{
'item_id': product.sku || String(product.id),
'item_name': product.name,
'item_brand': product.brand || '',
'item_category': product.categoryIds?.[0] ? String(product.categoryIds[0]) : '',
'price': product.price
}]
}
});
});
}
});
Common Errors
| Error | Cause | Fix |
|---|---|---|
| Store page views not tracked | Ecwid navigates without browser page loads | Use Ecwid.OnPageLoaded callback instead of GTM page view triggers |
| Duplicate ecommerce events | Both Ecwid's built-in GA4 and manual tracking are active | Disable the built-in integration if using custom JavaScript API tracking |
Ecwid is not defined error |
Script runs before Ecwid widget finishes loading | Wrap API calls in Ecwid.OnAPILoaded.add(function() { ... }) |
| Cart events fire on every render | OnCartChanged triggers on page load, not just user actions |
Compare cart state with a stored previous state to detect actual changes |
| Purchase event missing order data | OnOrderPlaced not firing on payment redirect |
Use server-side webhook (order.created) as a backup for redirect-based payments |
| Cross-domain tracking broken | GA4 linker not configured for Ecwid checkout domain | Add all Ecwid domains (including app.ecwid.com) to the linker configuration |
| Product price shows 0 | Product has variants and no base price | Access page.product.defaultDisplayedPrice instead of page.product.price |
| GTM not available on plan | GTM integration requires Business plan or higher | Upgrade plan or use manual script injection on the host page instead |
| Events fire in Ecwid admin preview | Store preview in admin panel triggers tracking callbacks | Check window.location for admin URLs and skip tracking in preview mode |
| Currency mismatch in analytics | Multi-currency store sends prices in display currency, not base | Use order.currency from the callback and set GA4 currency accordingly |
Performance Considerations
- Widget load time: The Ecwid widget script (
app.ecwid.com/script.js) is ~150KB gzipped. It loads asynchronously and does not block the host page's initial render, but it delays store interaction readiness - API callback overhead: Keep
OnCartChangedhandlers lightweight. This callback fires frequently (on every cart state change including quantity updates), and heavy processing delays the cart UI response - Host page impact: Adding analytics scripts to the host page adds to the total page weight. Use GTM to manage all tracking tags and defer non-essential pixels
- Lazy loading the store: If the Ecwid store is below the fold, defer widget initialization with
Ecwid.init()called on scroll or click rather than on page load - Image optimization: Ecwid serves product images through its CDN with automatic resizing. Ensure you use the CDN URLs (
d2j6dbq0ber1h5.cloudfront.net) rather than original uploaded images - Instant Site performance: Ecwid's hosted Instant Site includes its own analytics overhead. If using both Instant Site and an embedded store, deduplicate tracking to avoid counting users twice
- Script execution order: Ecwid's API callbacks are not available until the widget script loads. Always wrap tracking code in
Ecwid.OnAPILoaded.add()to ensure the API is ready before attaching event listeners
Plan-Based Analytics Features
| Plan | GA4 Integration | GTM | Meta Pixel | JavaScript API | REST API |
|---|---|---|---|---|---|
| Free | No | No | No | Limited | No |
| Venture | Yes | No | Yes | Full | Read-only |
| Business | Yes | Yes | Yes | Full | Full |
| Unlimited | Yes | Yes | Yes | Full | Full |
If your plan does not support GTM, you can still add GA4 directly via the built-in integration or by placing the gtag.js snippet on the host page. The JavaScript API callbacks work on all paid plans.