How to Use Variable Custom Goals in Plausible-Hugo: Complete Implementation Guide

Track custom events in Plausible Analytics using dynamic, data-driven goal names by leveraging Hugo's templating system and the plausible_js.html partial.

The divinerites/plausible-hugo module provides a lightweight integration for Plausible Analytics in Hugo sites. Beyond basic pageview tracking, it supports variable custom goals—dynamic event names generated from data files, page parameters, or site variables—enabling scalable analytics for large sites without hardcoding JavaScript for every individual action.

Understanding the Architecture

The integration relies on three core components defined in layouts/partials/plausible_head.html:

  1. plausible_head.html – The engine that constructs the Plausible script URL, initializes the window.plausible queue, and conditionally injects custom goal logic.
  2. plausible_js.html – A user-created partial where you define JavaScript helper functions. This is rendered as raw JavaScript (| safeJS) inside plausible_head.html, allowing full use of Hugo templating to generate dynamic function names and calls.
  3. Front-matter plausible_custom_goal – A declarative key that triggers an automatic goal event when a specific page loads.

Both mechanisms respect the global site.Params.plausible.enable setting and respect user privacy via the plausible_do_not_track page parameter.

Method 1: JavaScript Helper Functions

For one-off custom events triggered by user interactions, create reusable JavaScript functions.

Creating the Helper Partial

Create a file at layouts/partials/plausible_js.html in your site root:

// layouts/partials/plausible_js.html
function ClickOnTelephoneNumber() {
    plausible('ClickOnTelephoneNumber');
}

This partial is automatically detected by plausible_head.html and injected into the page's <script> block, making the ClickOnTelephoneNumber function available globally.

Invoking Goals in Templates

Reference the function in any Hugo template using an onclick handler:

<a href="tel:+331234567890"
   onclick="ClickOnTelephoneNumber()">
   +33 1 23 45 67 90
</a>

When a visitor clicks the link, the helper function fires the plausible() call with the specified goal name.

Method 2: Automatic Page-Level Goals

To track a goal immediately when a page loads—useful for "thank you" pages or specific landing pages—use front-matter configuration.

Add the plausible_custom_goal key to any content file:

---
title: "Contact Confirmation"
plausible_custom_goal: "ContactFormSubmitted"
---

During the build, plausible_head.html checks for this key. If present, it renders:

<script>
    plausible('ContactFormSubmitted');
</script>

This executes immediately after the Plausible script initializes, capturing the event without requiring user interaction.

Method 3: Dynamic Variable Custom Goals from Data

The most powerful feature is generating goal names dynamically from Hugo data files or site parameters. This enables tracking hundreds of unique events—such as clicks on different phone numbers or downloads of specific files—without manually writing JavaScript for each.

Setting Up the Data Source

Create a data file at /data/locations.yml:

locations:
  - name: "Paris Office"
    plausible_goal: "ParisPhone"
    phone: "+33 1 23 45 67 89"
  - name: "London Office"
    plausible_goal: "LondonPhone"
    phone: "+44 20 7946 0958"

Generating Functions Dynamically

In layouts/partials/plausible_js.html, loop through the data to create unique functions:

{{- $data := index .Site.Data .Site.Language.Lang }}
{{- if $data.locations }}
  {{- range $data.locations.locations }}
    function {{ .plausible_goal | safeJS }}Click() {
        plausible('{{ .plausible_goal | safeJS }}');
    }
  {{- end }}
{{- end }}

This generates JavaScript functions named ParisPhoneClick() and LondonPhoneClick() at build time.

In your template, invoke these dynamically:

{{- $data := index .Site.Data .Site.Language.Lang }}
{{- if $data.locations }}
  {{- range $data.locations.locations }}
    {{ $phone_clean := replaceRE "(\\s)" "" .phone }}
    <a href="tel:{{ $phone_clean }}"
       onclick="{{ .plausible_goal | safeJS }}Click()">
       {{ .phone }}
    </a>
  {{- end }}
{{- end }}

Each phone link now triggers a distinct Plausible goal based on the data file, enabling granular analytics without code duplication.

Integration with Other Tracking Features

Variable custom goals work alongside Plausible's built-in extensions. Enable additional tracking in your config.toml:

[params.plausible]
enable = true
domain = "example.com"
outbound_link = true
file_downloads = true

plausible_head.html automatically appends .outbound-links and .file-downloads to the script URL when these flags are active. Your custom goals will fire alongside these automatic events without conflict.

Summary

  • JavaScript Helpers: Create layouts/partials/plausible_js.html to define reusable onclick functions for interactive events.
  • Page-Level Goals: Use the plausible_custom_goal front-matter key to fire goals automatically when a page loads.
  • Variable Goals: Leverage Hugo's range loops and data files in plausible_js.html to generate dynamic goal names and functions at build time.
  • Conditional Loading: All goal mechanisms respect the global enable flag and the plausible_do_not_track page parameter.

Frequently Asked Questions

How do I disable tracking on specific pages while keeping custom goals active elsewhere?

Add plausible_do_not_track: true to the front-matter of any page. The plausible_head.html partial checks for this parameter and skips injecting the Plausible script and any custom goal logic for that specific page.

Can I use Hugo variables inside the plausible_js.html partial?

Yes. The partial is rendered with | safeJS inside plausible_head.html, which means you can use any Hugo templating syntax—such as {{ .Params.custom_goal }}, {{ .Site.Data }}, or range loops—to dynamically generate JavaScript function names and goal identifiers at build time.

What happens if I define both a front-matter goal and a JavaScript helper on the same page?

Both will execute. The front-matter plausible_custom_goal fires immediately when the page loads, while JavaScript helpers fire when the specific event (e.g., onclick) occurs. Ensure the goal names are distinct to avoid duplicate events in your Plausible dashboard.

Is there a limit to how many variable custom goals I can generate from a data file?

There is no hard limit imposed by the plausible-hugo module. However, since all goal functions are rendered into the HTML at build time, generating thousands of goals could increase page weight. For high-volume scenarios, consider using a single dynamic goal with custom properties (if supported by your Plausible version) or grouping similar actions under broader goal categories.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →