Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Minimal Analytics Snippet
(function (context, trackingId, options) {
const history = context.history;
const doc = document;
const nav = navigator || {};
const storage = localStorage;
const encode = encodeURIComponent;
const pushState = history.pushState;
const typeException = 'exception';
const generateId = () => Math.random().toString(36);
const getId = () => {
if (!storage.cid) {
storage.cid = generateId()
}
return storage.cid;
};
const serialize = (obj) => {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
if(obj[p] !== undefined) {
str.push(encode(p) + "=" + encode(obj[p]));
}
}
}
return str.join("&");
};
const track = (
type,
eventCategory,
eventAction,
eventLabel,
eventValue,
exceptionDescription,
exceptionFatal
) => {
const url = 'https://www.google-analytics.com/collect';
const data = serialize({
v: '1',
ds: 'web',
aip: options.anonymizeIp ? 1 : undefined,
tid: trackingId,
cid: getId(),
t: type || 'pageview',
sd: options.colorDepth && screen.colorDepth ? `${screen.colorDepth}-bits` : undefined,
dr: doc.referrer || undefined,
dt: doc.title,
dl: doc.location.origin + doc.location.pathname + doc.location.search,
ul: options.language ? (nav.language || "").toLowerCase() : undefined,
de: options.characterSet ? doc.characterSet : undefined,
sr: options.screenSize ? `${(context.screen || {}).width}x${(context.screen || {}).height}` : undefined,
vp: options.screenSize && context.visualViewport ? `${(context.visualViewport || {}).width}x${(context.visualViewport || {}).height}` : undefined,
ec: eventCategory || undefined,
ea: eventAction || undefined,
el: eventLabel || undefined,
ev: eventValue || undefined,
exd: exceptionDescription || undefined,
exf: typeof exceptionFatal !== 'undefined' && !!exceptionFatal === false ? 0 : undefined,
});
if(nav.sendBeacon) {
nav.sendBeacon(url, data)
} else {
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.send(data);
}
};
const trackEvent = (category, action, label, value) => track('event', category, action, label, value);
const trackException = (description, fatal) => track(typeException, null, null, null, null, description, fatal);
history.pushState = function (state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({ state: state });
}
setTimeout(track, options.delay || 10);
return pushState.apply(history, arguments);
}
track();
context.ma = {
trackEvent,
trackException
}
})(window, "XX-XXXXXXXXX-X", {
anonymizeIp: true,
colorDepth: true,
characterSet: true,
screenSize: true,
language: true
});
@ihorvorotnov
Copy link

ihorvorotnov commented Dec 16, 2020

Depends when the newer API is finalized, currently in alpha.. 🤔

Is it?

https://developers.google.com/analytics/devguides/reporting/core/v4/migration#v4_1

@idarek
Copy link

idarek commented Jan 2, 2021

I see that the last David response was 2 years ago. Is that project dead?

@DavidKuennen
Copy link
Author

DavidKuennen commented Jan 6, 2021

Thanks for all the responses and sorry for not responding sooner. I don't have the capacity to improve this snipped further, but you can fork/use it as you like.

@einleid2506
Copy link

einleid2506 commented Jan 14, 2021

Hello. Please help me.
I used to track my bounce rate with the following code:
setTimeout(function(){ga('send', 'event', 'Bonuce-Rate', 'time_on_page_more_than_15_sec', location.pathname);}, 15000);
Now I am using your script as the site is much faster.
But I don’t understand programming and don’t know how to adjust the bounce rate to take into account visits where the time spent on the page is more than 15 seconds.
There is such a line in your code, but I don’t know whether it’s this or not?
setTimeout(track, options.delay || 10);

or before </script> i need to add something like this:
setTimeout("ma.trackEvent('Bonuce-Rate', 'time_on_page_more_than_15_sec')", 15000);

?

@idarek
Copy link

idarek commented Jan 19, 2021

Thanks for all the responses and sorry for not responding sooner. I don't have the capacity to improve this snipped further, but you can fork/use it as you like.

Thanks David,

ps. to people looking into v4. As I read, this is totally new Analytics and when moved into that, previous data is not transferred hence don't see the point to migrate from Universal to v4 unless you need to.

For current version, I would be interested in finding some workaround for blocking /collect by various scripts and adblockers.

Even Google web.dev website reporting as follow

/collect (www.google-analytics.com) | Failed to load resource: net::ERR_BLOCKED_BY_CLIENT.Inspector

@dmitrizzle
Copy link

dmitrizzle commented May 2, 2021

For those looking to send pageviews, here are the changes to the above code:

const track = (
    type,
    eventCategory,
    eventAction,
    eventLabel,
    eventValue,
    exceptionDescription,
    exceptionFatal,
    pageviewPathname // update function signature to accept pathname
  ) => {
      dl:
        doc.location.origin +
        (pageviewPathname ||
          doc.location.pathname + doc.location.search), // optionally add pathname when provided
  const trackEvent = (category, action, label, value) =>
    track("event", category, action, label, value);
  const trackException = (description, fatal) =>
    track(typeException, null, null, null, null, description, fatal);
  const trackPageview = pathname => // add paveview track function
    track("pageview", null, null, null, null, null, null, pathname);
context.ma = {
    trackEvent,
    trackException,
    trackPageview, // add trackPageview function to the utility object
  };

In use:

window.ma.trackPageview( "/my-page")

Note that the above changes do not cancel the initial pageview or window.history events. The function parameter accepts relative pathnames without the domain name.

@rmoscuba
Copy link

rmoscuba commented Sep 16, 2021

Hi. I am trying to get the clientId, but the cookie set by google is taking tooo long for appear. Is there a way to set this mannually? Isnt it just random numbers? Thanks.

@filz51
Copy link

filz51 commented Nov 11, 2021

How should I modify the snippet to record Page Load Time plt?

@idarek
Copy link

idarek commented Mar 17, 2022

ps. to people looking into v4. As I read, this is totally new Analytics and when moved into that, previous data is not transferred hence don't see the point to migrate from Universal to v4 unless you need to.

So, Google will force us to move out from Universal Analytics to GA4: Google Analytics ending support for Universal Analytics in 2023 after over a decade.

Anybody been experimenting with GA4 in minimal form?

@wpsumo
Copy link

wpsumo commented Mar 17, 2022

I would have hoped google by the end of 2022 would have offered a migration over to GA4.

And the 99kb JavaScript a minimal version, yes please!

@idarek
Copy link

idarek commented Mar 18, 2022

99kB???

For somebody who is interested in this aspect any further, here are my thoughts
Google Universal Analytics property is shutting down. Here is what you need to know

@wpsumo
Copy link

wpsumo commented Mar 18, 2022

It's very strange to me that the analytics team at Google force into an analytics that should fit everyone. Far from all want cross-platform tracking like mobile apps etc. To not migrate the simple data as traffic, sources, events etc is eye-opening. Giving users a reason to move away, the leverage is otherwise you are “locked” so you rather just stay to not lose any data.

It's just data points to export and import, I hope there will come a solution as soon as we get closer to a migration + preferably a lighter version. Sill bound into google data studio and other products, so wouldn't change to alternatives.

But something that crossed my mind, will they stop with gtag.js as well-meaning the version 3? Only the universal is stopping, or gtag v3 as well? It's lighter, would require adjustment in script and event tracking etc as things are a bit different.

Analytics 44kb
gtag 66kb.
gtag v4 91kb or more

@idarek
Copy link

idarek commented Mar 18, 2022

The worst thing is that new JS weights 171kB

@brielov
Copy link

brielov commented Mar 18, 2022

Just go with plausible guys, I have it self-hosted and it is awesome. The script is around 1kb.

@wpsumo
Copy link

wpsumo commented Mar 18, 2022

@brielov The problem to go outside google products is you have no smooth data studio integration to merge data and sources. You have no Google Search Console integration in Plausible, etc.

When selling products or sites or hiring staff which is related to attribution or website tracking, it's in favour to have the well known commercial tracking system Google Analytics.

But I agree it's a good alternative in terms of privacy and lightweight script, but not for me at the moment. Still keeping my hope for a light script for Google Analytics and migration option. Hope the force to GA4 will force google to make it lighter and find a data migration option.

@idarek
Copy link

idarek commented Mar 31, 2022

On this website there is in detail described URL+Payload for Google Analytics 4.

the URL changed from /collect/ to /g/collect/, the v attribute to value 2 etc.
I tried to change some elements. Add missing ones (changed t to en and pageview to page_view) and use a new tracking code (for testing G-EGREM0V9JQ) but without luck.

Hope somebody better in JavaScript will be able to figure this out.

There is new bit gtm there and _p that is not described and is the reason why collect is not sending its payload.
Interesting is that when I send RAW url (changing cid and sid numbers)
https://www.google-analytics.com/g/collect?v=2&tid=G-EGREM0V9JQ&gtm=2oe3n1&_p=2086482769&sr=1920x1080&ul=en&cid=526699612.1648651312&_s=1&dl=https%3A%2F%[2F](http://2f/)%2F&dt=My%20Hugo%20Playground&sid=1648651312&sct=1&seg=1&en=page_view&_ss=1

It will collect traffic in GAv4 so there is a progress, but now need to put this together into the above code, hence need somebody to help with that.

@idarek
Copy link

idarek commented Apr 1, 2022

To all who follow this discussion, I am following up on my post above (which makes me think about how to make it).
I am currently working on Google Analytics 4 minimal snipped and I got already development version working and collecting traffic. Still need to do some adjustments and will publish it... so there will be an option 🙌

@idarek
Copy link

idarek commented Apr 2, 2022

Hi All,
I think I made it. Here is my post (with code and reference to Gist) about A very Minimal Google Analytics 4 Snippet

@wkingnet
Copy link

wkingnet commented May 4, 2022

Hi All, I think I made it. Here is my post (with code and reference to Gist) about A very Minimal Google Analytics 4 Snippet

Great, I just needed it. thank you very much for your research

@someguy9
Copy link

someguy9 commented May 7, 2022

Thank you @idarek!

@sifigi4335
Copy link

sifigi4335 commented May 18, 2022

A better minimal analytics for GA4 https://github.com/jahilldev/minimal-analytics

@idarek
Copy link

idarek commented May 18, 2022

A better minimal analytics for GA4 https://github.com/jahilldev/minimal-analytics

It's a different approach. More options to choose from is better for users, but leave the interpretation of which one is better to people who will be using it.

@sifigi4335
Copy link

sifigi4335 commented May 18, 2022

A better minimal analytics for GA4 https://github.com/jahilldev/minimal-analytics

It's a different approach. More options to choose from is better for users, but leave the interpretation of which one is better to people who will be using it.

I am using it, and in my opinion, it is better. So, I don't know what your problem is.

@idarek
Copy link

idarek commented May 18, 2022

I am using it, and in my opinion, it is better. So, I don't know what your problem is.

The only problem is the one that you creating. I appreciate that @jahilldev found a way to extend the functionality of my initial work to give users what they may need. You are the opposite. You are the first to complain about somebody's work and the valuable time that they put into it but you are the last person to offer any advice or even pointing into solution how to make it better.

The script that I come up with taught me a lot, especially since I am not a developer in that matter so that was all new for me and I appreciate that I made something useful and learn a lot. With @jahilldev implementation, we will be able to extend it and give users some alternatives that are hard to find. I will be looking forward to seeing with the @jahilldev approach how I can extend my way or achieve it.

A simple word thank you is enough and motivates you to work forward, but your kind of attitude does the opposite.

So thank you @someguy9 @wkingnet for appreciating my work and thank @jahilldev for your time to extend the functionality.

The top appreciation goes to @DavidKuennen who initiate it and make it popular, thanks to which we can work further with GA4, and @thyngster who provided Google Analytics 4 Measurement Protocol CheatSheet that allowed understanding some of the functionality.

@sifigi4335
Copy link

sifigi4335 commented May 23, 2022

I am using it, and in my opinion, it is better. So, I don't know what your problem is.

The only problem is the one that you creating. I appreciate that @jahilldev found a way to extend the functionality of my initial work to give users what they may need. You are the opposite. You are the first to complain about somebody's work and the valuable time that they put into it but you are the last person to offer any advice or even pointing into solution how to make it better.

The script that I come up with taught me a lot, especially since I am not a developer in that matter so that was all new for me and I appreciate that I made something useful and learn a lot. With @jahilldev implementation, we will be able to extend it and give users some alternatives that are hard to find. I will be looking forward to seeing with the @jahilldev approach how I can extend my way or achieve it.

A simple word thank you is enough and motivates you to work forward, but your kind of attitude does the opposite.

So thank you @someguy9 @wkingnet for appreciating my work and thank @jahilldev for your time to extend the functionality.

The top appreciation goes to @DavidKuennen who initiate it and make it popular, thanks to which we can work further with GA4, and @thyngster who provided Google Analytics 4 Measurement Protocol CheatSheet that allowed understanding some of the functionality.

You are the one who jumped into my comment when I didn't even mention your name, No? I will not entertain your butthurt feelings just coz you were stubborn to implement a feature that I needed, but that someone else picked up and acted on. I will not entertain your comments since this comment section is derailing from featuring the great work of David and his contribution to the community. Cheers!

@idarek
Copy link

idarek commented May 23, 2022

"coz you were stubborn to implement a feature that I needed"

There is no I in a team.
Requesting a feature vs trying to find how to implement it are two different things.
As mentioned - zero input and plenty of complaints on others' work.
My lack of knowledge (that I admit) of how to implement some things doesn't mean that I don't want to implement them.

Wish you best

@wpsumo
Copy link

wpsumo commented Jun 1, 2022

Haven't jumped on the GA4 train yet but how do you guys track events inline via onclick? And is this supported with minimal analytics v4? I read there were some limits on how many unique parameters and length you can create? This be a problem just all new, I assume this is more to setup the types of events it is? So if you have parameters "conversion" can I then have evenCategry + eventAction + eventLabel?

As an example I used this (page not needed as this can normally be matched with the normal page value in GA):
onclick="ga('send', 'event', 'eventCategory', 'eventAction', 'eventLabel');"

onclick="ga('send', 'event', 'WhatTypeOfCta', 'WhereCtaIs', 'CtaPath');"

So anyone with a good article or who can share how inline onclick event tracking work in new GA4 and we can use it with minimal analytics?

@jahilldev
Copy link

jahilldev commented Jun 1, 2022

@wpsumo I haven't been able to find official documentation (it must exist, somewhere..) about doing this programmatically, only via GTM, which is obviously not ideal given the "minimal" goals of this.

After profiling the official GA4 library, I've discovered there's another default event type of click, which you can use in conjunction with event params, e.g ?ep.custom_param=lorem or if it's a number, ?epn.price=5.

If you're using @minimal-analytics/ga4, and you want a custom click event, you'll need to add an event handler to the element, something like this:

import { track } from '@minimal-analytics/ga4';

const button = document.querySelector('button'); // use relevant selector

button.addEventListener('click', () => track({ type: 'click', event: { 'epn.product_id': 12345 } }));

The above assumes you're using a node build tool or environment to generate your site. There currently isn't a way to access track(); without this. Open to feature requests though...

I am in the process of implementing automatic anchor and button click tracking in @minimal-analytics/ga4, which is another "out of the box" event provided by the official GA4 library. That will trigger a click event with a series of event params when a user clicks an anchor or button element.

Hope that helps!

@wpsumo
Copy link

wpsumo commented Jun 28, 2022

@jahilldev Thanks for your answer, no not using any node environment. I run wordpress so php environment, I wish to be able to continue using inlined event similar to how I've used onclick today as I have a better structure there than track all click via class or ID.

As you say I did not find anything online or in the official documentation regarding this. Hopefully it will come before 2023 :D and then it can be added into minimal GA4 as well. For now if you know a solution or find, please share.

I'm not a fan of tracking all links external or internal. In this case I prefer to write them static in the html while they are dynamic via php. So it looks more like below in structure.

The only custom params I need to create if I prefer is "converted" but not necessary the rest is values which should be unlimited. The problem I see if trying to do it inline. But I have not spent hours into GA4 as I haven't seen this being supported in the official documentation yet... so no point to trying GA4 out for me right now, waiting for it though.

Event:
Converted/CTA (Category - Static)
Which CTA on the page (Action - dynamic php)
Where did the CTA go/slug (Label - dynamic php)

@jahilldev
Copy link

jahilldev commented Jun 28, 2022

@wpsumo One solution to this would be to add track to the window in @minimal-analytics/ga4, that way you'd be able to call the function in the same way you've been doing with Universal Analytics.

I'm not super keen about blanket window pollution, but maybe I could add a config option that would do this.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment