Integrating Google Analytics with Vaadin Flow: A Step-by-Step Guide

I wrote a short blog post: Integrating Google Analytics with Vaadin Flow: A Step-by-Step Guide - Keep IT Simple with Simon Martinelli

9 Likes

:+1:

It is bit nasty that it is kind of hard to make this kind of integrations without (ab)using the Component API (which is designed for UI components and bound to Element on the client side).

BTW. Did you consider using the GoogleAnalyticsTracker add-on?

I tried the plugin but never got it to work.

What was the issue? Didn’t see a bug report :nerd_face:

I don’t use it myself, but I’m ready to investigate/contribute, I see that very relevant add-on for many apps.

It never reported any page views to Google Analytics, and as the JS code is straightforward, I use that.

I tested the add-on and found out the instructions were for older version. Google Analytics has been changing over the years many times and the instructions weren’t keeping up with that.

Current default is to use annotation and automatic tracking with Router:

@PageTitle("MainView")
@Route("")
@EnableGoogleAnalytics("G-VF84NR52Q0")
public class MainView extends VerticalLayout {

and then you can track the custom events e.g. in button click like this:

GoogleAnalyticsTracker.getCurrent()
        .sendEvent("eventgroup","customevent");

And one more note: since the last update was for Vaadin 8 version it is offered by default in Directory. So if you want to use the add-on:

Version 5.x for Vaadin 24+
and
Version 3.x for Vaadin 8.0

Anyway, thanks for the good article! It makes sense and the basic functionality is pretty straight-forward to implement like that if want to keep the code in your project.

1 Like

I actually don’t see any reason for why that functionality needs to be tied to a Component since all it does is to run JS that isn’t tied to any DOM element. page.executeJs could probably work just as well while being more semantically clear.

For sure, it doesn’t have to be a component. It was just more straightforward to explain to my colleagues that they have to add the GoogleAnalytics “component”.

1 Like

I really miss the concept of “extension” from V8. It would be really handy to separate non-visible components from the visible components that need layout space.

I’ve tried Simon’s version from the blog post with an Vaadin 24 app but unfortunately I always get

Uncaught (in promise) ReferenceError: sendToAnalytics is not defined

I debug a little with the browser toolos (F12): The analytics script was loaded from google via

analyticsScript.src = 'https://www.googletagmanager.com/gtag/js?id'= + analyticsId but
afterwards the function call failed. I tried with localhost:8080 and deployed version in case of any sandbox restrictions, but same result.

With the version 5.0.0 from the add-on it works out of the box.

That’s very strange because this runs fine in production.

I’ll check tomorrow

I checked it but did not get any errors, and it works.
Could you provide a reproducible example?

Hello! As far as I remember, the Google services do not work with the localhost. You also need to specify the domains from which the requests will be made. Perhaps something has changed.

Did your tests @SimonMartinelli works at localhost ?

Furthermore: there is a param of type JsonObject in

public void sendEvent(String eventName, JsonObject eventParams)

Which FQCN ? I’ve used elemental.json.JsonObject…

Yes this is elemental.json

But your error happens when the script is starting.

I don’t get any errors on localhost.

So, I’ve created a MVE (minimal viable example)

  • npm init vaadin
  • add all according the blog post
  • start locally
  • same error in browser console

But sseems that I can’t upload that zip file here ???

Sorry, the file you are trying to upload is not authorized (authorized extensions: jpg, jpeg, png, gif, heic, heif, webp, avif, mov, mp4, webm, m4v, mpeg).

And rename the zip to png doens’t work neither

So … I made it and put it on Google Drive :see_no_evil:

Hi @SimonMartinelli ,

could you reproduce the error with the MVE in ga-test.zip ?

Greetings from Saxonia
Dominik

Can you try with this code? Also, make sure that your GA ID is valid; otherwise, it doesn’t download the JavaScript from Google.

@Tag("google-analytics")
public class GoogleAnalytics extends Component implements HasSize {

    public GoogleAnalytics(String measurementId) {
        getElement().executeJs(
            """
                    (function(browserWindow, htmlDocument, scriptTagName, analyticsLayerName, analyticsId) {
                        // Initialize analytics data layer array if it doesn't exist
                        browserWindow[analyticsLayerName] = browserWindow[analyticsLayerName] || [];

                        // Add initial GTM event with timestamp
                        browserWindow[analyticsLayerName].push({
                            'gtm.start': new Date().getTime(),
                            event: 'gtm.js'
                        });

                        // Get reference to first script tag in document
                        var firstScriptTag = htmlDocument.getElementsByTagName(scriptTagName)[0];

                        // Create new script element for Analytics
                        var analyticsScript = htmlDocument.createElement(scriptTagName);

                        // Set custom layer name if not using default 'dataLayer'
                        var customLayerParam = analyticsLayerName != 'dataLayer' ? '&l=' + analyticsLayerName : '';

                        // Configure script loading
                        analyticsScript.async = true;
                        analyticsScript.src = 'https://www.googletagmanager.com/gtag/js?id=' + analyticsId + customLayerParam;

                        // Insert Analytics script into document
                        firstScriptTag.parentNode.insertBefore(analyticsScript, firstScriptTag);

                    })(window, document, 'script', 'dataLayer', '$0');

                    // Ensure dataLayer exists
                    window.dataLayer = window.dataLayer || [];

                    // Function to send data to Analytics
                    function sendToAnalytics() {
                        window.dataLayer.push(arguments);
                    }

                    // Initialize Analytics with timestamp
                    sendToAnalytics('js', new Date());

                    // Configure Analytics with measurement ID
                    sendToAnalytics('config', '$0');
                    """,
            measurementId);
    }

    public void sendPageView(String pageName) {
        getElement().executeJs("""
                gtag('event', 'page_view', {
                 'page_title': $0,                      // The page name you pass
                 'page_location': window.location.href, // Full URL
                 'page_path': window.location.pathname  // URL path only
                });
                """, pageName);
    }

    public void sendEvent(String eventName, JsonObject eventParams) {
        getElement().executeJs("""
                gtag('event', $0, $1); // $0 is event name, $1 is parameters object
                """, eventName, eventParams);
    }

}

Hi Simon,

thank you for your response.
With your new version, the same error occur, but here my findings:

  1. the request for this GA script from google was tried with:
    https://www.googletagmanager.com/gtag/js?id=$0
    It seems, that the function parameter with the GA-Tag was not resolved. I take a look and found

})(window, document, 'script', 'dataLayer', '$0');

I removed the single quotes and changed this into

})(window, document, 'script', 'dataLayer', $0);

The same with the following line, also removing the quotes
sendToAnalytics('config', '$0');

After those changes, the browser console still complains for every view change
Uncaught (in promise) ReferenceError: gtag is not defined

but the view change is reported to GA and visible on the real time board, so it seems to work.

Best regard
Dominik