Add Minimal Analytics (Google Analytics v4) to Hugo

There are efforts made to create stripped down versions of Google Analytics version 4. This helps to speed up your site instead of loading the GA4 official library script.

However, the stripped down versions support the basic or essential tracking functions, and thus, if you require more advanced tracking (like Adwords), just use the official GA4 library instead.

Currently, I have found two implementations of the minimal GA4 script:

  1. jahilldev / minimal-analytics/ga4 - this version tracks page views, engagement time, scroll, file download and click tracking events… It weighs in at 2kb (gzipped).
  2. idarek / minimal-analytics-4-min.js - this version tracks pageviews, scroll and site search and weighs slightly over 1kb (gzipped).

This tutorial uses the code from the first implementation (the CDN section). Ensure you have the latest Hugo version (from version 0.100.0 and up).

N.B. 1 I recommend using methods 1 and 3.

N.B. 2 You might need to add https://www.google-analytics.com to your connect-src directive in Content Security Policy to make this code work (method 1 also requires using hash or nonce if you disable inline scripts in your CSP).

Method 1

  1. Create an analytics.html file inside layouts/partials and paste the code below
Summary
{{- with site.Params.google.analytics }}
{{- $script := resources.GetRemote "https://cdn.jsdelivr.net/npm/@minimal-analytics/ga4/dist/index.js" }}
<script >
window.minimalAnalytics = {
  trackingId: '{{ . }}',
  autoTrack: true, // <-- init tracking
};
</script>
<script src="{{ $script.RelPermalink }}" async></script>
{{- end }}
  1. Add the code below to your configuration file (this example assumes config.toml is in use) and replace G-XXXXX with your GA4 measurement ID.
[params.google]
analytics = "G-XXXXX"
  1. Then add the partial wherever you want it to appear in your site (e.g. inside <head></head> or just before the closing </body> tag)
{{- partial "analytics.html" . }}
  1. Check your source code (ctrl+U) to ensure your code appears correctly.

If your site is live, or even if using a test site, check the “real time” section of your GA4 property. If you implemented everything correctly, you should start seeing some live stats being recorded after a few minutes.

Method 2

  1. Copy-paste the code below in a js file (like track.js) in assets/js folder of your base layout/theme.
Summary
window.minimalAnalytics = {
  trackingId: '{{ . }}',
  autoTrack: true, // <-- init tracking
};
  1. Add this code to the analytics.html partial.
Summary
{{- with site.Params.google.analytics }}
{{- $script := resources.GetRemote "https://cdn.jsdelivr.net/npm/@minimal-analytics/ga4/dist/index.js" }}
{{ $track := resources.Get "/js/track.js" | resources.ExecuteAsTemplate "js/track.js" . }}
{{- $analytics := slice $track $script | resources.Concat "js/analytics.js" | minify | fingerprint }}
<script src="{{ $analytics.RelPermalink }}" integrity="{{ $analytics.Data.Integrity }}" async></script>
{{- end }}
  1. Include the tracking code in the configuration file as in method 1.
  2. Add the partial in your template as in method 1.

Method 3

  1. track.js
Summary
import params from "@params";

window.minimalAnalytics = {
  trackingId: params.trackingId,
  autoTrack: true, // <-- init tracking
};
  1. analytics.html
Summary
{{- with site.Params.google.analytics }}
{{- $js := resources.GetRemote "https://cdn.jsdelivr.net/npm/@minimal-analytics/ga4/dist/index.js" }}
{{- $track := resources.Get "track.js" -}}
{{- $opts := dict 
  "params" (dict "trackingId" site.Params.google.analytics) 
-}}
{{- $track = $js | js.Build $opts -}}
{{- $analytics := slice $track $script | resources.Concat "js/analytics.js" | minify | fingerprint }}
<script src="{{ $analytics.RelPermalink }}" integrity="{{ $analytics.Data.Integrity }}" async></script>
{{- end }}
  1. Include the tracking code in the configuration file as in method 1.
  2. Add the partial in your template as in method 1.

(Credit)

Side note

If you want this code to only work in production, wrap the <script> part in hugo.IsProduction, e.g

Summary
{{ if hugo.IsProduction }}
<script src="{{ $analytics.RelPermalink }}" integrity="{{ $analytics.Data.Integrity }}" async></script>
{{ end }}
2 Likes

You can also just replace the default analytics file by creating /layouts/_internal/google_analytics.html.

  • This way you can just use the default GA param of Hugo googleAnalytics = ""
  • If the theme is using Hugo’s default GA call, you don’t need to add a call to a custom partial. Makes switching themes much easier.

I was avoiding having to interrupt internal template functions. But anyone can use your feature if they prefer it.

I have created mine as a universal approach, so can be used by passing in <head> of any site. If we want to integrate it with Hugo’s config file, that’s not too difficult.

Just in minified script replace G-XXXXXXXXXX with reference to config file with {{ .Site.Params.google.analytics }} and then put this in a config file

[params.google]
analytics = "G-XXXXXXXXXX"

5 posts were split to a new topic: Error: can’t evaluate field ConCat in type interface

Hi,
Worth to add to the first post that James solution is using TypeScrypt and mine is using JavaScript Vanilla. Both great, and I see that James implements a lot of good things in his which I am trying to implement in mine as well along with my learning curve.

ps. Just updated mine to version 1.09 with scroll tracking if you want to update as well.

Scrolls
Capture scroll events each time that a visitor gets to the bottom of a page.

1 Like

The CDN section uses vanilla JS, which I used in this tutorial. If you can, refactor your code to split the tracking part from the main code as he has, I will redo the tutorial to include your code.

I implemented this with pleasure only to discover the same night that Austria and France have banned GA already.

I don’t think there is a work around for that so I guess we have to do without it?

1 Like

If it is due to GDPR matters, then that’s beyond the scope of this script. You can google how to make GA4 GDPR friendly.