How to Enable Hash-Based Routing in Plausible-Hugo

Set hash_based_routing = true in your Hugo site's [params.plausible] configuration to append .hash to the Plausible script URL, enabling tracking of URL fragments in single-page applications.

The Plausible-Hugo theme provides native support for hash-based routing, a critical feature for single-page applications (SPAs) and client-side routing where URL fragments (the portion after #) must be tracked. When enabled, the theme automatically modifies the tracking script's filename to include the .hash suffix, instructing Plausible's backend to capture and record these fragments alongside standard page-view data.

What Is Hash-Based Routing?

Hash-based routing allows Plausible Analytics to capture URL fragments—everything following the # symbol in a URL. In standard web analytics, browsers do not send URL fragments to the server, so traditional tracking scripts miss this navigation data. By loading script.hash.js instead of script.js, Plausible's tracker parses the fragment and includes it in the page-view payload, ensuring complete path tracking for SPAs built with frameworks like React, Vue, or Alpine.js.

Enabling Hash-Based Routing in Your Hugo Configuration

Step 1: Configure the Site Parameter

Open your Hugo configuration file (typically config.toml at the project root) and locate the [params.plausible] table. Add the hash_based_routing parameter and set it to true:

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

According to the repository's default configuration in config.toml, this parameter exists but is commented out by default, allowing you to uncomment it when needed.

Step 2: Rebuild and Verify

Run hugo or hugo server to rebuild your site. The layouts/partials/plausible_head.html partial will detect the enabled parameter and render the script tag with the .hash suffix:

<script defer data-domain="example.com"
        src="https://plausible.io/js/script.hash.js"></script>

To confirm hash-based routing is active, navigate to a URL containing a fragment (such as /#/dashboard) and check your Plausible dashboard. The page-view entry should display the full path including the fragment, rather than just /.

How the Hash-Based Routing Implementation Works

The logic resides in layouts/partials/plausible_head.html, where the theme constructs the script URL dynamically. When site.Params.plausible.hash_based_routing evaluates to true, the partial appends the string .hash to the filename variable:

{{- $hash := "" }}
{{- if site.Params.plausible.hash_based_routing }}
{{- $hash = ".hash" }}
{{- end }}
{{- $pio_script = printf "%s%s%s%s" $pio_script $downloads $outbound $hash }}

This conditional assignment ensures that $hash contains either an empty string (default) or .hash (when enabled). The final printf statement concatenates this suffix onto the base script name, transforming script.js into script.hash.js before injection into the HTML head.

Configuration Examples

Standard Plausible.io Domain

For sites using the default Plausible analytics domain:

[params.plausible]
domain = "your-site.com"
enable = true
hash_based_routing = true

Generates:

<script defer data-domain="your-site.com"
        src="https://plausible.io/js/script.hash.js"></script>

Custom JavaScript Domain

When using a custom CDN or self-hosted Plausible instance via the custom_js_domain parameter:

[params.plausible]
domain = "your-site.com"
custom_js_domain = "stats.your-cdn.com"
enable = true
hash_based_routing = true

This configuration produces index.hash.js instead of script.hash.js, as implemented in the source logic:

<script defer data-domain="your-site.com"
        src="https://stats.your-cdn.com/misc/js/index.hash.js"></script>

Summary

  • Hash-based routing captures URL fragments for single-page applications by loading a specialized Plausible script variant.
  • Enable the feature by setting hash_based_routing = true in the [params.plausible] section of config.toml.
  • The partial layouts/partials/plausible_head.html dynamically appends .hash to the script filename when the parameter is enabled.
  • Resulting script URLs become script.hash.js or index.hash.js, depending on whether you use the standard domain or a custom JavaScript domain.
  • Verification requires checking that Plausible records the full URL including the # fragment in your analytics dashboard.

Frequently Asked Questions

Does enabling hash-based routing affect performance?

No, enabling hash-based routing does not impact site performance or loading times. The only difference is the filename of the fetched script (script.hash.js versus script.js). The file size and execution behavior remain identical; Plausible's backend simply processes the URL fragment data when the .hash suffix is present.

Can I use hash-based routing with a self-hosted Plausible instance?

Yes, hash-based routing works with both Plausible.io and self-hosted instances. When using a custom domain via the custom_js_domain parameter, the theme automatically generates index.hash.js instead of script.hash.js, ensuring compatibility with self-hosted deployments that use the misc/js/ path structure.

What happens if I enable hash-based routing on a traditional multi-page site?

Enabling the feature on a traditional site causes no harm or data loss. The .hash script variant will simply record fragment data when present (such as anchor links like #section-2) while continuing to track standard page views normally. If your site does not use URL fragments for routing, the analytics data remains unchanged compared to the standard script.

Where is the hash-based routing logic implemented in the source code?

The implementation resides in layouts/partials/plausible_head.html within the divinerites/plausible-hugo repository. Specifically, the conditional logic checks site.Params.plausible.hash_based_routing and sets the $hash variable to .hash when enabled, which is then concatenated into the final script URL via the printf function.

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 →