Category: Google Analytics

Google Analytics 4 setup guides, tracking configurations, and measurement strategies for better data-driven decisions.

  • How to Set Up GA4 Key Event Tracking in Squarespace (For Therapy Practices)

    This comprehensive guide addresses a common challenge for therapy practice websites: measuring meaningful client interactions rather than just traffic volume.

    Why Key Event Tracking Matters

    Therapy practices need different tracking than e-commerce businesses since consultations happen offline. Without key event tracking, you're making decisions based on traffic numbers alone when conversion patterns matter more.

    Understanding which pages and traffic sources actually lead to consultation requests is far more valuable than knowing how many people visited your homepage.

    What Counts as Key Events

    Primary events for therapy practices include:

    • Contact form submissions — the most direct indicator of interest
    • Phone clicks — especially important for mobile visitors
    • Consultation bookings — if you use online scheduling
    • Newsletter signups — for nurturing potential clients

    Essentially, any action that moves someone closer to becoming a client should be tracked as a key event.

    Step-by-Step Setup Process

    Step 1: Connect GA4 to Squarespace

    In your Squarespace dashboard, go to Settings > Advanced > External API Keys and add your GA4 Measurement ID (the one that starts with G-).

    Step 2: Enable Enhanced Measurement

    In GA4, navigate to Admin > Data Streams > Your website stream, and ensure Enhanced Measurement is turned on. This automatically tracks basic interactions like form submissions.

    Step 3: Mark Events as Key Events

    In GA4, go to Configure > Events. Find the form_submit event and toggle it as a key event. This tells GA4 that form submissions are important conversions for your business.

    Step 4: Test Your Setup

    Submit a test form on your website, then check GA4's Realtime report to verify the event appears. If it doesn't show within a few minutes, double-check your Measurement ID.

    Phone Click Tracking

    For mobile visitors who tap your phone number, you'll need to add a small code snippet. In Squarespace, go to Settings > Advanced > Code Injection and add this to the Footer section:

    document.querySelectorAll('a[href^="tel:"]').forEach(function(link) {
        link.addEventListener('click', function() {
            gtag('event', 'phone_click', {
                'event_category': 'Contact',
                'event_label': this.href
            });
        });
    });
    

    Then mark phone_click as a key event in GA4.

    Practical Applications

    Once tracking is set up, you can discover patterns like:

    • Which service pages convert better than others
    • Which traffic sources generate actual consultation requests
    • What time of day people are most likely to reach out
    • Whether mobile or desktop visitors convert more often

    This data helps you allocate marketing resources more effectively.

    Common Issues and Solutions

    Event not appearing: Double-check your Measurement ID format (should be G-XXXXXXXXXX)

    Can't distinguish between forms: If you have multiple forms, you may need custom event tracking to differentiate them

    Traffic showing as "Direct": This often means UTM parameters aren't set up correctly on your marketing campaigns

    Getting Started

    Start with form submission tracking — it takes about 15 minutes to set up. Then add phone click monitoring and schedule monthly automated reports to review conversion patterns and refine your marketing focus.

  • UTM Parameters for Google Analytics 4: A Practical Guide

    UTM parameters are intended to simplify GA4 tracking, but they often create messy, fragmented data due to inconsistent naming conventions across marketing teams. This guide helps you implement them correctly.

    What GA4 Already Knows

    GA4 automatically classifies certain traffic types without UTM tags:

    • Organic search — when someone finds you through Google, Bing, etc.
    • Direct visits — when someone types your URL directly
    • Referrals — when someone clicks a link from another website

    Adding UTMs to these can actually override GA4's accurate classification, creating data problems instead of solving them.

    When UTMs Are Necessary

    UTMs become essential for traffic where the referrer doesn't clearly indicate intent:

    • Paid social ads (Facebook, Instagram, LinkedIn)
    • Email campaigns
    • SMS messages
    • Influencer partnerships
    • Affiliate links
    • QR codes
    • Sponsored content

    Without UTMs, this traffic often gets lumped into "Direct" or misattributed.

    Understanding Source vs. Medium

    Source identifies where the click originated — the platform name like "instagram" or "newsletter"

    Medium describes the traffic type — such as "paid_social" or "email"

    GA4 treats these values as case-sensitive and distinct, so "Instagram" is different from "instagram" in your reports. This is why consistency matters so much.

    The Consistency Problem

    UTM values should be lowercase, spelled out, and standardized across your organization. Once inconsistent data enters GA4, cleanup becomes extremely difficult or impossible.

    Common problems include:

    • Shortened platform names (fb vs. facebook)
    • Inconsistent capitalization (Email vs. email vs. EMAIL)
    • Missing medium parameters
    • Applying paid search conventions to social advertising

    Practical Recommendations

    For Paid Social Ads

    utm_source=instagram
    utm_medium=paid_social
    utm_campaign=winter_promo
    

    For Organic Social Posts

    utm_source=instagram
    utm_medium=organic_social
    

    This separation lets you compare paid vs. organic performance.

    For Email Campaigns

    utm_source=newsletter
    utm_medium=email
    utm_campaign=january_tips
    

    General Rules

    • Always use lowercase
    • Use underscores for multi-word values (paid_social, not paid-social)
    • Be descriptive but concise
    • Document everything

    Building a UTM Standard

    Organizations should establish a shared naming standard documented in an accessible location — whether that's a shared spreadsheet, Notion doc, or project management tool.

    Include:

    • Approved source values for each platform
    • Standard medium values
    • Campaign naming conventions
    • Who's responsible for creating tracked links

    This ensures all team members use identical values when creating tracked links, and your GA4 data stays clean and actionable.

    Common Mistakes to Avoid

    1. Adding UTMs to organic search links — let GA4 handle this automatically
    2. Using different values for the same platform — pick one and stick with it
    3. Forgetting the medium parameter — source alone isn't enough context
    4. Using spaces in values — always use underscores instead
    5. Not documenting your standards — new team members will guess wrong

    Get your UTM strategy right from the start, and your analytics will actually tell you what's working.

  • GA4 Add-to-Cart Tracking for Fonteva: A Starter

    Organizations that use Fonteva, such as associations, nonprofits, and membership groups, sell registrations, memberships, and products through a cart, even if the site doesn’t feel like a typical store.

    GA4’s e-commerce events are the best way to measure this activity and send conversions to Google Ads, LinkedIn, or your ad platform of choice. The problem is that Fonteva doesn’t send GA4 events by default, and it's a single-page app (SPA), so “fire on /cart” pageview rules don't work.

    This guide shows how to implement GA4’s add_to_cart in Google Tag Manager (GTM) without changing Fonteva’s code. We watch page elements and SPA route changes, send GA4’s recommended parameters, and avoid duplicates. It’s a first step toward full-funnel tracking (view_cart, begin_checkout, purchase).

    The ideal long-term approach is a first-party data layer in Fonteva or Lightning, but this gets you started now.

    Glossary

    • SPA (Single-Page App): The site loads once and then swaps screens with JavaScript instead of loading new pages.

    • Route: The screen an SPA shows. In Fonteva, the route usually includes everything after the # in the URL, for example #/store/browse/detail/….

    • SPA Hop: A screen change inside the app without a full page load. You see the #… route change.

    • Cart Modal (Mini-Cart): A pop-up that appears on the product page immediately after an add. It confirms the add and offers View Cart or Checkout.

    • Stash: A temporary snapshot of the item a user is adding (id, name, price, quantity). We save it at click time to know what the user selected.

    • Clear: This is the step where we immediately remove the stashed snapshot after sending GA4, so later actions do not double-count.

    • Session Storage (sessionStorage): Small, per-tab storage in the browser that disappears when the tab closes. It’s perfect for our short-lived, per-interaction stash.

    Why This Works for Fonteva

    • Fonteva often keeps users on the product route and opens a cart modal after an add. If you only watch for a cart URL, you miss the real add moment or count it late.

    • We store the selection on click to preserve the correct item, price, and quantity, even if the DOM changes.

    • We fire add_to_cart when the cart modal is visible, which is the first reliable signal that the add succeeded.

    • We clear the stash immediately to prevent duplicate fires when someone later clicks View Cart or navigates into the cart.

    What We’ll Build

    • Variables that assemble GA4-ready items[], value, and a simple pending flag.

    • Two small HTML tags will be used to store the item on click and clear it after sending GA4.

    • A GA4 Event tag for add_to_cart.

    • Triggers that are SPA-safe: Element Visibility for the cart modal and History Change as a fallback.

    Everything below is ordered for a clean implementation and QA path. Names are suggestions; consistency helps.

    One-Time GTM Updates

    Enable these in GTM → Variables → Configure:

    • Click Element, Click Text, Click Classes, Click Target

    • New History Fragment, Old History Fragment, New/Old History State, History Source

    • Page URL, Page Path, Page Hostname, Event

    Note: The page URL does not include the hash. Use the New History Fragment to test routes like #/store/browse/detail/…

    Step 1: Create Variables

    1) Currency (Constant)

    • Name: Currency – Constant

    • Value: USD

    • Reason: GA4 expects a currency with monetary events.

    2) Pending Flag (To Check if in The Middle Of An Add?)

    Custom JavaScript variable JS – Fonteva Pending Add (bool):

    function(){
      try { return sessionStorage.getItem('fonteva_pending_add') === '1'; }
      catch(e){ return false; }
    }
    

    Reason: Gates the modal and history triggers so they only fire when a product add just started.

    **3) Product Items Array (for **

    add_to_cart

    )

    Custom JavaScript variable JS – Fonteva Items Array:

    function () {
      try {
        var frag = (location.hash || '').replace(/^#/, '');
        if (/^\/store\/(cart|checkout)(\/|$)/i.test(frag)) { return []; }
    
        var s = sessionStorage.getItem('fonteva_item');
        if (s) { var o = JSON.parse(s); if (o && o.item_id) return [o]; }
      } catch (e) {}
    
      var nameEl = document.querySelector('.pfm-detail_discount_label');
      var rawName = nameEl ? nameEl.textContent.trim() : (document.title || '(unknown)');
      var parts = rawName.split(' - ');
      var variant = parts.length > 1 ? parts.pop().trim() : undefined;
      var baseName = parts.length ? parts.join(' - ').trim() : rawName;
    
      var priceEl = document.querySelector('.pfm-detail_discount_price .FrameworkCurrencyField') ||
                    document.querySelector('.pfm-details_sub_price .FrameworkCurrencyField');
      var price;
      if (priceEl && priceEl.textContent) {
        var p = priceEl.textContent.replace(/[^0-9.]/g, '');
        price = p ? parseFloat(p) : undefined;
      }
    
      var qty = 1;
      var selects = Array.prototype.slice.call(
        document.querySelectorAll('.pfm-detail_quantity select.slds-select')
      );
      if (selects.length) {
        var sel = selects.find(function(s){ return s.offsetParent !== null; }) || selects[selects.length-1];
        var v = (sel.value || '').trim();
        if (/^other$/i.test(v)) {
          var wrap = sel.closest('.pfm-detail_quantity') || document;
          var inp = wrap.querySelector('input[type="number"], input[type="text"]');
          if (inp && inp.value) {
            var n = inp.value.replace(/[^0-9]/g, '');
            qty = n ? parseInt(n, 10) : 1;
          }
        } else {
          var n2 = v.replace(/[^0-9]/g, '');
          qty = n2 ? parseInt(n2, 10) : 1;
        }
      }
    
      var idMatch = location.href.match(/\/(?:detail|merch_new)\/([^/?#]+)/i);
      var itemId = idMatch && idMatch[1] ? decodeURIComponent(idMatch[1])
                                         : baseName.replace(/\s+/g, '_').toLowerCase();
    
      var item = { item_id: itemId || '(unknown)', item_name: baseName || '(unknown)', quantity: qty };
      if (typeof price === 'number') item.price = price;
      if (variant) item.item_variant = variant;
      return [item];
    }
    

    Reason: GA4’s add_to_cart expects items[]. We provide a single object for the item being added.

    4) Product Value (Price × Quantity)

    Custom JavaScript variable JS – Fonteva Value:

    function () {
      try {
        var frag = (location.hash || '').replace(/^#/, '');
        if (/^\/store\/(cart|checkout)(\/|$)/i.test(frag)) { return undefined; }
      } catch(e) {}
    
      try {
        var s = sessionStorage.getItem('fonteva_item');
        if (s) {
          var o = JSON.parse(s);
          var q = parseInt(o && o.quantity, 10) || 1;
          var p = (o && typeof o.price === 'number') ? o.price : undefined;
          if (typeof p === 'number' && isFinite(p)) {
            var v = p * q;
            return Math.round(v * 100) / 100;
          }
        }
      } catch(e) {}
    
      try {
        var priceEl = document.querySelector('.pfm-detail_discount_price .FrameworkCurrencyField') ||
                      document.querySelector('.pfm-details_sub_price .FrameworkCurrencyField');
        var price;
        if (priceEl && priceEl.textContent) {
          var num = priceEl.textContent.replace(/[^0-9.]/g, '');
          price = num ? parseFloat(num) : undefined;
        }
        var qtyVar = {{JS - Fonteva Quantity}}; // insert via GTM picker if present
        var q2 = parseInt(qtyVar, 10) || 1;
        if (typeof price === 'number' && isFinite(price)) {
          return Math.round(price * q2 * 100) / 100;
        }
      } catch(e) {}
    
      return undefined;
    }
    

    Reason: GA4 monetization reports and Ads imports rely on a numeric value.

    Step 2: Add Two Small HTML Tags

    A) GA4 – Product Stash (Custom HTML)

    Attach to the product button click trigger in the next step.

    
    (function () {
      try {
        var now = Date.now();
        var lastTs = parseInt(sessionStorage.getItem('fonteva_item_ts') || '0', 10);
        var pending = sessionStorage.getItem('fonteva_pending_add') === '1';
        if (pending && (now - lastTs) 
    

    Reason: We snapshot the user’s selection at click time to send the correct data when the modal confirms the add.

    B) GA4 – Product Clear (Custom HTML)

    No trigger of its own; we will sequence it after the GA4 event.

    
    try {
      sessionStorage.removeItem('fonteva_item');
      sessionStorage.removeItem('fonteva_pending_add');
      sessionStorage.removeItem('fonteva_item_ts');
    } catch(e){}
    

    Reason: Clearing immediately prevents double-counting when someone later opens the cart.

    Step 3: Create Triggers

    1) Product Add Button (For Stash Only)

    Trigger name: Click – Add to Cart – General

    Type: Click – All Elements → Some Clicks

    Click Element matches CSS selector:

    .pfm-details button[data-name="addToCart"],
    .pfm-details button.FrameworkButton[aria-label="Add to Order"],
    .pfm-details button[data-label="Add to Order"],
    .pfm-details button[data-name="addToCart"] *,
    .pfm-details button.FrameworkButton[aria-label="Add to Order"] *
    

    Reason: We only stash here. No GA4 events fire on this click.

    2) Cart Modal Visible (ensures the add to cart completes and is captured)

    Trigger name: Add to Cart – Cart Modal Visible

    Type: Element Visibility

    • Element selector: .addToCartModal .LTEShoppingCart, .slds-modal__content .LTEShoppingCart

    • Fire: Once per element

    • Minimum percent visible: 1

    • Minimum on-screen duration: 200 ms

    • Observe DOM changes: checked

    Some visibility events (AND):

    • New History Fragment contains /store/browse/detail/

    • JS – Fonteva Pending Add (bool) equals true

    Reason: Many Fonteva adds happen without a route change. The modal confirms the add succeeded.

    3) History Fallback (safety net, de-dupe)

    Trigger name: Add to Cart – History

    Type: History Change → Some History Changes

    • History Source equals hashchange

    • New History Fragment matches RegEx ^/store/(cart|checkout)(/|$)

    • JS – Fonteva Pending Add (bool) equals true

    Reason: It caters to edge cases where a route change confirms the addition. Restricting to hashchange prevents duplicate fires.

    Step 4: Build The GA4 Event

    Tag name: GA4 – add_to_cart

    Type: GA4 Event

    • Event name: add_to_cart

    Parameters:

    • items → {{JS – Fonteva Items Array}}

    • value → {{JS – Fonteva Value}}

    • currency → {{Currency – Constant}}

    Triggers (OR):

    • Add to Cart – Cart Modal Visible

    • Add to Cart – No History

    Tag Sequencing:

    • Fire a tag after → GA4 – Product Clear

    • Setup tag → none

    In GA4 → Admin → Events, mark add_to_cart as a Key Event and import it into Ads platforms.

    QA Checklist

    • On the product page, select a quantity that is not one and click Add to Order. Only GA4 – Product Stash should fire.

    • When the cart modal appears, GA4 – add_to_cart should fire on the Element Visibility event, then GA4 – Product Clear.

    • GA4 DebugView should show add_to_cart with a populated items[0] object, a numeric value, and currency set to USD.

    • Clicking View Cart should not fire another add_to_cart.

    If the GA4 tag does not fire on the modal, open the Element Visibility event, click the tag, and use Why Not. Confirm the selector matches your modal, and the New History Fragment contains /store/browse/detail/. Ensure that Observe DOM changes is checked.

    What This Unlocks

    • Real add-to-cart data in GA4 Monetization reports, ready to mark as a Key Event and sync to Google Ads and LinkedIn.

    • A pattern you can extend to view_cart and begin_checkout using cart-level variables, and purchase on the confirmation page with a transaction ID (ideally via a data layer push for accuracy).

    Why This Matters

    For membership-driven organizations, registrations and dues often fund the mission. GA4 is powerful, but only when standard events flow in with item-level detail. This GTM build provides reliable add-to-cart data without a development sprint and is fully compatible with a future first-party data layer.

    Recap — Everything We Added

    Variables

    • Currency – Constant (USD)

    • JS – Fonteva Pending Add (bool)

    • JS – Fonteva Items Array

    • JS – Fonteva Value

    Tags

    • GA4 – Product Stash (Custom HTML)

    • GA4 – add_to_cart (GA4 Event)

    • GA4 – Product Clear (Custom HTML; sequenced after GA4 event)

    Triggers

    • Click – Add to Cart – General (stash only)

    • Add to Cart – Cart Modal Visible (Element Visibility)

    • Add to Cart – History

    If you want this implemented, QA’d, or documented for your organization, we can help.

  • How to Set an Event in GA4 for Cal.com Booking Confirmations

    Tracking successful bookings on your website is essential for understanding user behavior and measuring the effectiveness of your marketing efforts. In Google Analytics 4 (GA4), these bookings are considered conversions or key events. Accurately tracking these events allows you to measure how well your website is performing, optimize your campaigns, and ultimately drive more business.

    When you use Cal.com to manage your calendar and bookings, setting up GA4 to track these events can be challenging. The confirmation page URL contains a parameter called isSuccessBookingPage=true but tracking that string using a standard page view trigger is difficult because the app uses dynamic URLs and the history API to update URLs on the backend.

    During troubleshooting, we discovered that Cal.com already sends an event to the data layer when a booking is confirmed. By configuring this event in Google Tag Manager (GTM), you can ensure that it populates GA4 correctly and captures these important conversions.

    Setting Up the GA4 Event in Google Tag Manager

    Step 1: Create a Custom Event Trigger in GTM

    To ensure that the GA4 event is triggered when the bookingSuccessful (the cal.com confirmation) event occurs, follow these steps:

    • In GTM, navigate to Triggers.

    • Click on New and select Trigger Configuration.

    • Choose Custom Event as the trigger type.

    • In the Event Name field, enter bookingSuccessful.

    • Set the trigger to fire on All Custom Events.

    • Save the trigger as Booking Successful Trigger.

    Step 2: Set Up a GA4 Event Tag

    Now that you have your trigger set up, it’s time to create the GA4 event tag:

    • Go to Tags in GTM and click on New.

    • Choose Google Analytics: GA4 Event as the tag type.

    • Select your GA4 configuration tag, which should include your Measurement ID.

    • In the Event Name field, enter booking_confirmation.

    • Assign the Booking Successful Trigger to this tag.

    • (Optional) Add event parameters if you want to capture additional data from the bookingSuccessful event.

    • Save the tag.

    Step 3: Test Your Setup

    Before publishing your changes, it’s important to test the setup:

    • Re-enter Preview mode in GTM.

    • Complete another booking on your Cal.com page.

    • Verify that the GA4 event tag fires when the bookingSuccessful event is pushed to the data layer.

    Step 4: Publish Your Changes

    Once you’re confident that the event is being tracked correctly, go back to GTM and click on Submit to publish your changes. This will make the event tracking live on your website.

    Sending the Event to Google Ads

    Tracking bookings in GA4 is essential, but you may also want to send this event to Google Ads for conversion tracking. You can do this in two ways: by importing events from GA4 into Google Ads or by using a Google Ads Conversion Tracking tag in GTM.

    Method 1: Importing Events from GA4 into Google Ads

    • Link GA4 to Google Ads: Ensure your GA4 property is linked to your Google Ads account. You can do this in GA4 by going to Admin > Google Ads Linking.

    Import Conversion:

    • In your Google Ads account, go to Tools & Settings > Conversions.

    • Click on New Conversion Action and select Import.

    • Choose Google Analytics 4 properties and select Web.

    • Select the booking_confirmation event from GA4 that you want to track as a conversion.

    • Complete the setup by defining the conversion action details, such as the conversion name and value.

    • Track Conversions in Google Ads: Once set up, conversions will be automatically tracked in Google Ads whenever the booking_confirmation event is triggered.

    Method 2: Using a Google Ads Conversion Tracking Tag in GTM

    Create a Conversion Action in Google Ads:

    • Go to Tools & Settings > Conversions in Google Ads.

    • Click New Conversion Action and choose Website.

    • Set up the conversion action, including the conversion name, category, value, and other details.

    • Google Ads will provide you with a Conversion ID and Conversion Label.

    Create a Google Ads Conversion Tracking Tag in GTM:

    • Go to Tags in GTM and click on New.

    • Choose Google Ads Conversion Tracking as the tag type.

    • Enter the Conversion ID and Conversion Label provided by Google Ads.

    • Assign the Booking Successful Trigger you created earlier to this tag.

    • Save the tag.

    Test and Publish:

    • Use GTM’s Preview mode to test that the Google Ads Conversion Tracking tag fires correctly on the booking confirmation.

    • Once confirmed, publish your changes.

    Troubleshooting Tips

    Why URL Parameters Might Not Work

    Using URL parameters to track booking confirmations in Cal.com can be unreliable because the platform uses the History API to update the URL dynamically. This means that traditional Page View triggers may not capture the event correctly. Instead, rely on the bookingSuccessful event that Cal.com has already sent to the data layer.

    Other Troubleshooting Steps

    If the booking_confirmation event isn’t showing up in your GA4 real-time reports, consider the following:

    • Measurement ID: Ensure that the Measurement ID in your GA4 configuration tag is correct.

    • Real-time Report Delay: Sometimes, the event takes a few minutes to appear in GA4 real-time reports.

    • DebugView: Use GA4’s DebugView mode to see if GA4 is processing the event.

    • GTM and Google Ads Configuration: Ensure that all configurations in GTM and Google Ads are correctly set up, and that you’ve linked your accounts properly.

    Verify the Cal.com bookingSuccessful Event

    If you run into problems, you'll want to verify that cal.com is still sending the bookingSuccessful event.

    You can skip this step because as of the date of this post, cal.com does send this event.

    Before setting up the event in GA4, you need to confirm that the bookingSuccessful event is being pushed to the data layer by cal.com. Here’s how:

    • Go to Google Tag Manager (GTM) and enter Preview mode.

    • Complete a booking on your Cal.com page.

    • In the GTM Debug panel, look for the bookingSuccessful event in the left column.

    Once you've confirmed this event, you're ready to move on to creating the trigger in GTM.


    Need help with GA4 or Google Tag Manager setup? We offer analytics consulting to get your tracking right.