Microsoft Clarity’s default tracking captures page views, clicks, and basic engagement signals. That’s enough for blog and content sites. For ecommerce, it’s nowhere near enough — you need to know which cart actions correlate with which session behaviors, which payment methods correlate with checkout abandonment, and which product categories drive rage-clicks on quantity selectors.
Custom tags are Clarity’s mechanism for attaching ecommerce-specific events to recorded sessions. Done well, they let you filter recordings by “sessions where user added 3+ items but abandoned at payment” or “sessions where user changed quantity 5+ times.” Those filtered views are where actionable CRO insights live.
This guide is the complete custom tag implementation framework for ecommerce in Microsoft Clarity. The tag taxonomy we deploy for Dallas ecommerce clients on Shopify, WooCommerce, and custom stacks. Code examples for every common cart action. Performance considerations for not tanking Core Web Vitals. And the 6 implementation mistakes that produce noisy or useless tag data.
Microsoft Clarity custom tags add ecommerce-specific metadata to session recordings, enabling powerful filtering like “sessions where cart value > $200 but checkout abandoned” or “sessions that used PayPal vs credit card.” The right tag taxonomy covers: cart actions (add, remove, quantity change), funnel position (browse, cart, checkout step), commerce context (cart value, item count, category), and session outcome (purchase, abandonment with reason). Implementation via clarity("set", "tag", value) API. The framework below covers the full taxonomy, code patterns for Shopify/WooCommerce/custom stacks, INP-safe implementation, and how to act on the resulting filtered analyses.
What Custom Tags Actually Do in Clarity
Clarity custom tags are key-value pairs you attach to a session via JavaScript API calls. They’re searchable, filterable, and visible in the session details view. The API:
// Set a single tag
clarity("set", "cart_value", "247.50");
// Identify a user (use hashed identifier, not email)
clarity("identify", "user_hash_abc123");
// Set custom tag with array of values
clarity("set", "products_viewed", ["sku-001", "sku-042", "sku-099"]);
Once set, the tag appears in the session’s metadata and can be used to filter sessions in the dashboard. The filter syntax: “Show me sessions where cart_value > 200 AND session ended without purchase.”
This is dramatically more useful than the default Clarity setup because it lets you isolate high-value behavior patterns rather than wading through generic session lists.
Use short, structured values for tags. clarity("set", "cart_step", "shipping") is filterable. clarity("set", "cart_note", "User reached shipping page after browsing 4 products") is unfilterable narrative text. Treat tags like database fields: short, structured, predictable values. Keep descriptive text in comments or other systems.
The Tag Taxonomy for Ecommerce
Across Dallas ecommerce implementations, this taxonomy covers 90%+ of useful filtering needs:
Funnel position tags
| Tag name | Values | Set when |
|---|---|---|
page_type | home, category, product, cart, checkout, success | Every page load |
checkout_step | contact, shipping, billing, payment, review | Each checkout step |
funnel_progress | browser, cart_adder, checkout_starter, payment_reacher | On qualifying action |
Cart action tags
| Tag name | Values | Set when |
|---|---|---|
cart_value_bucket | under_50, 50_to_200, 200_to_500, 500_to_1500, over_1500 | Cart updates |
cart_item_count | 1, 2, 3, 4, 5_plus | Cart updates |
cart_has_high_ticket | yes, no | When cart contains item over $500 |
cart_quantity_changes | 0, 1_to_3, 4_to_10, 10_plus | Track quantity edits |
Commerce context tags
| Tag name | Values | Set when |
|---|---|---|
customer_type | guest, returning, vip | On checkout entry |
payment_method_attempted | card, paypal, apple_pay, google_pay, affirm | Payment step |
discount_applied | yes, no | Cart updates |
shipping_method | standard, express, white_glove, pickup | Shipping step |
primary_category | furniture, electronics, apparel, etc. | Cart updates |
Outcome tags
| Tag name | Values | Set when |
|---|---|---|
session_outcome | purchase, abandoned_cart, abandoned_checkout, abandoned_payment, browsing | Session end / exit intent |
abandonment_step | contact, shipping, billing, payment, review | If abandoned |
error_encountered | none, validation, payment_declined, shipping, generic | On error event |
Implementation on Shopify
For Shopify stores, the easiest pattern uses theme JavaScript with Liquid variable interpolation. Add to theme.liquid in the head:
<script>
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "YOUR_PROJECT_ID");
// Set page_type based on Shopify template
{% if template == 'product' %}
clarity("set", "page_type", "product");
{% elsif template == 'cart' %}
clarity("set", "page_type", "cart");
{% elsif template contains 'checkout' %}
clarity("set", "page_type", "checkout");
{% elsif template == 'index' %}
clarity("set", "page_type", "home");
{% elsif template contains 'collection' %}
clarity("set", "page_type", "category");
{% endif %}
// Customer type
{% if customer %}
clarity("set", "customer_type", "returning");
clarity("identify", "{{ customer.id | sha256 | slice: 0, 16 }}");
{% else %}
clarity("set", "customer_type", "guest");
{% endif %}
// Cart-level data on cart and checkout pages
{% if cart.item_count > 0 %}
<script>
(function() {
var value = {{ cart.total_price | divided_by: 100.0 }};
var bucket = "under_50";
if (value > 1500) bucket = "over_1500";
else if (value > 500) bucket = "500_to_1500";
else if (value > 200) bucket = "200_to_500";
else if (value > 50) bucket = "50_to_200";
clarity("set", "cart_value_bucket", bucket);
var count = {{ cart.item_count }};
var countBucket = count >= 5 ? "5_plus" : String(count);
clarity("set", "cart_item_count", countBucket);
})();
</script>
{% endif %}
</script>
For dynamic cart updates (AJAX add-to-cart), hook into Shopify’s cart fetch API and re-set tags after every cart update.
Implementation on WooCommerce
For WooCommerce, use the wp_head hook in your theme’s functions.php:
function clarity_custom_tags() {
if (!function_exists('WC')) return;
$cart = WC()->cart;
$total = $cart ? $cart->get_total('edit') : 0;
$item_count = $cart ? $cart->get_cart_contents_count() : 0;
$bucket = 'under_50';
if ($total > 1500) $bucket = 'over_1500';
else if ($total > 500) $bucket = '500_to_1500';
else if ($total > 200) $bucket = '200_to_500';
else if ($total > 50) $bucket = '50_to_200';
$page_type = 'home';
if (is_product()) $page_type = 'product';
else if (is_cart()) $page_type = 'cart';
else if (is_checkout()) $page_type = 'checkout';
else if (is_shop() || is_product_category()) $page_type = 'category';
$customer_type = is_user_logged_in() ? 'returning' : 'guest';
?>
<script>
clarity("set", "page_type", "<?php echo $page_type; ?>");
clarity("set", "customer_type", "<?php echo $customer_type; ?>");
<?php if ($item_count > 0): ?>
clarity("set", "cart_value_bucket", "<?php echo $bucket; ?>");
clarity("set", "cart_item_count", "<?php echo min($item_count, 5) === 5 ? '5_plus' : (string)$item_count; ?>");
<?php endif; ?>
</script>
<?php
}
add_action('wp_head', 'clarity_custom_tags', 100);
For checkout step tracking, hook into WooCommerce’s checkout actions:
// On checkout review page (Step 4)
add_action('woocommerce_review_order_before_payment', function() {
echo '<script>clarity("set", "checkout_step", "payment");</script>';
});
// On thank-you page
add_action('woocommerce_thankyou', function($order_id) {
$order = wc_get_order($order_id);
$method = $order->get_payment_method();
?>
<script>
clarity("set", "session_outcome", "purchase");
clarity("set", "payment_method_attempted", "<?php echo esc_js($method); ?>");
</script>
<?php
});
Implementation on Custom Stacks (React, Vue, etc.)
For custom-built ecommerce (Next.js, Vue, React), wrap Clarity calls in a utility module:
// utils/clarity.js
export function clarity(...args) {
if (typeof window !== 'undefined' && window.clarity) {
window.clarity(...args);
}
}
export function tagPageType(type) {
clarity("set", "page_type", type);
}
export function tagCartUpdate(cart) {
const total = cart.total;
let bucket = "under_50";
if (total > 1500) bucket = "over_1500";
else if (total > 500) bucket = "500_to_1500";
else if (total > 200) bucket = "200_to_500";
else if (total > 50) bucket = "50_to_200";
clarity("set", "cart_value_bucket", bucket);
clarity("set", "cart_item_count", cart.itemCount >= 5 ? "5_plus" : String(cart.itemCount));
}
export function tagCheckoutStep(step) {
clarity("set", "checkout_step", step);
}
export function tagSessionOutcome(outcome) {
clarity("set", "session_outcome", outcome);
}
// In your cart store/reducer:
import { tagCartUpdate } from '../utils/clarity';
function cartReducer(state, action) {
const newState = /* reducer logic */;
tagCartUpdate(newState);
return newState;
}
Tag values are searchable in the Clarity dashboard and stored long-term. Never include email addresses, phone numbers, names, or credit card data as tag values. For user identification, use clarity("identify", hashedId) with a SHA-256 hash of the user ID, not the raw email. PII in tags violates Clarity’s terms of service and GDPR; both will get you flagged and potentially delisted.
Performance Impact: Keeping INP Under 200ms
Each clarity("set", ...) call adds a tiny amount of work to the main thread. Individual calls are negligible. Batching them on every cart update can add up. Optimization patterns:
- Debounce cart-update tags. If a user rapidly changes quantity 5 times, set tags only after the last change (300ms debounce). Don’t set 5 cart_value_bucket tags in quick succession.
- Use
requestIdleCallbackfor non-critical tags. Wrap analytics calls inrequestIdleCallback(() => clarity("set", "primary_category", category))so they run during browser idle time. - Limit total tags per session. Clarity allows up to 50 tags. Aim for 10–15 for clean filtering. More than that adds dashboard noise.
- Don’t set tags in render-blocking paths. Page-type tags should be set after first paint, not inline in the <head> for synchronous-render content. Use the API after DOMContentLoaded.
- Cache tag values client-side. If
cart_value_buckethasn’t changed, don’t re-set it. Compare against previous value before calling the API.
Filtering and Acting on Tag Data
Once tags are flowing, the real value comes from the filtered analyses you can run. Examples of high-impact filter queries:
- “Show me sessions where cart_value > 500 AND session_outcome = abandoned_payment” — high-ticket payment abandonment, the highest-value cohort to study
- “Show me sessions where payment_method_attempted = paypal AND session_outcome = abandoned” — reveals if PayPal flow has bugs
- “Show me sessions where checkout_step = shipping AND device = mobile” — shipping form mobile friction
- “Show me sessions where cart_quantity_changes > 4” — users struggling with quantity selector UX
- “Show me sessions where customer_type = vip AND session_outcome = abandoned_cart” — high-value customers leaving without purchasing (urgent)
Each filtered view gives you 20–100 specific sessions to watch using the 5-step framework from our rage-click diagnosis guide. The combination of structured tagging + filtered analysis is where the conversion lift opportunities live.
Real Case: Dallas Apparel Brand Recovers 24% of High-Ticket Abandonment
In December 2025 we implemented this taxonomy for a Dallas-based women’s apparel brand (DTC, $6.4M ARR). Their pre-implementation state: Clarity installed, default config, no custom tags. They knew abandonment was a problem but couldn’t isolate which cohort or step.
Implementation:
- Added 11 custom tags following the taxonomy above
- Wired up Shopify theme.liquid + AJAX cart updates
- Server-side conversion tracking via Meta Conversions API for redundancy — see our Meta CAPI setup guide
First analysis (3 weeks of data):
- Filter “cart_value_bucket = 200_to_500 AND session_outcome = abandoned_payment” surfaced 87 sessions
- Watching 30 of these revealed: 19/30 abandoned on the credit card field (autofill broken)
- Filter “payment_method_attempted = apple_pay” surfaced 240 sessions, 89% completed (vs 58% completion for card)
Actions taken:
- Fixed autofill on credit card field (removed conflicting
autocomplete="off") - Made Apple Pay the primary mobile payment option (was below the fold previously)
- Added Google Pay alongside Apple Pay (was missing entirely)
The 6 Mistakes That Make Custom Tags Useless
- 1. Free-text values instead of buckets. Setting
cart_valueto"247.50"is unfilterable in practice (every session has a unique value). Use buckets like"200_to_500". - 2. Not setting tags after dynamic updates. Single-page app cart changes don’t trigger page loads. Hook into cart update events explicitly.
- 3. Setting page_type but not cart context. Knowing the page isn’t enough. The cart state, customer type, and funnel position are what enable useful filtering.
- 4. Tag spam. Setting 30+ tags per session creates dashboard noise without adding insight value. Stick to 10–15 well-chosen tags.
- 5. Inconsistent tag naming. Mix of
cart_value,cartValue,cart-valueacross pages makes filtering unreliable. Pick one naming convention (snake_case recommended) and enforce it. - 6. Not setting outcome tags. Without
session_outcomeset on every session, you can’t filter abandoners vs purchasers. This is the most valuable single tag and most often forgotten. Set it on the success page AND on exit-intent or session-end events.
Advanced Patterns Worth Considering
Once the base taxonomy is working, several advanced patterns unlock additional analysis:
- A/B test variant tagging. If you’re running tests, set
ab_test_variantto track which variant the user saw. Cross-reference with session outcomes to understand which variant produces which behaviors. - Discount code tagging. Set
discount_codeto which code was applied (not the value). Reveals which promotions drive completion vs cart-shopping behavior. - Time-of-day tagging. Set
visit_hour_bucket(morning, afternoon, evening, late_night). Reveals time-based patterns — some businesses see dramatically different conversion rates by time of day. - Source-medium tagging. Set
traffic_sourcebeyond what Clarity captures by default (e.g.,paid_google,organic_search,email_nurture_week3). Filter rage-clicks by source — paid traffic users have different expectations than organic. - Error event tagging. Set
error_encounteredwith the specific error type. Filter to sessions where errors occurred to diagnose recurring issues. Pair with the framework in JavaScript errors vs user frustration.
For Dallas ecommerce clients with $1M+ annual revenue, the custom tag taxonomy is one of the highest-ROI analytics investments available. Implementation takes 6–15 dev hours; the insights generated drive multi-quarter optimization roadmaps. The full implementation pairs with the broader audit framework in Clarity vs Hotjar in 2026 — tag-driven filtering is what makes either tool actually useful for serious CRO.
Frequently Asked Questions
How many custom tags can I set per session in Clarity?
Clarity allows up to 50 tags per session. In practice, aim for 10–15 well-chosen tags. More than 20 makes the dashboard noisy and degrades performance. Quality over quantity: a few well-structured tags with bucketed values unlock more filtering power than 30 random free-text tags.
Do I need to upgrade to Clarity Plus or another paid tier for custom tags?
No. Custom tags are available on the free tier of Microsoft Clarity, with no quota limits. Clarity remains free for unlimited traffic and unlimited sessions (Microsoft monetizes through Bing data, not subscriptions). If you’re comparing to Hotjar, this is a key reason teams switch to Clarity for ecommerce analytics specifically.
Can I retroactively add tags to existing sessions?
No — tags must be set during the live session. If you implement tags today, you start collecting filterable data going forward. Plan implementation to run for 3–6 weeks before drawing conclusions, especially for less-frequent behaviors (high-ticket abandonment). For analysis you need on historical data, you’ll need to rely on the default session metadata Clarity already captures (page, device, source).
How do custom tags interact with Consent Mode v2?
Clarity respects user consent state. If the user denies tracking consent, Clarity doesn’t record or tag the session. If consent is granted, tags work normally. If consent state changes mid-session (rare but possible), behavior depends on your Consent Management Platform’s integration. For Dallas clients targeting EU traffic, we recommend explicit consent banners that activate Clarity only after consent — not the default opt-out approach.
Will custom tags hurt my page speed?
Negligibly, if implemented correctly. Each clarity("set", ...) call adds <1ms to the main thread. The risk is in batched cart updates where you might fire 10 tags in 50ms during a rapid cart change. Use the debounce/idle-callback patterns above to avoid this. Total Interaction to Next Paint (INP) impact of a well-implemented tag system: under 5ms even on cart-heavy sessions.
Need help implementing Clarity custom tags?
We’ll deploy the full ecommerce tag taxonomy for your Shopify, WooCommerce, or custom stack — including INP-safe implementation, consent compliance, and dashboard setup for filtered analyses. Typical turnaround: 1–2 weeks.
Get a Clarity Setup Audit Explore Ecommerce SEO Services