From cf5d71557fdda8098c2ab62fccba59c74a07054e Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 24 Sep 2025 13:36:32 +1000 Subject: [PATCH 1/3] fix(useScriptEventPage): remove hooks on dispose --- src/runtime/composables/useScriptEventPage.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/runtime/composables/useScriptEventPage.ts b/src/runtime/composables/useScriptEventPage.ts index 08afe224..6dcce17f 100644 --- a/src/runtime/composables/useScriptEventPage.ts +++ b/src/runtime/composables/useScriptEventPage.ts @@ -1,5 +1,5 @@ import { useNuxtApp, useRoute, injectHead } from 'nuxt/app' -import { ref } from 'vue' +import { ref, onScopeDispose } from 'vue' import type { TrackedPage } from '#nuxt-scripts/types' export function useScriptEventPage(onChange?: (payload: TrackedPage) => void) { @@ -17,7 +17,7 @@ export function useScriptEventPage(onChange?: (payload: TrackedPage) => void) { let lastPayload: TrackedPage = { path: '', title: '' } let stopDomWatcher = () => {} // TODO make sure useAsyncData isn't running - nuxt.hooks.hook('page:finish', () => { + const stopPageFinishHook = nuxt.hooks.hook('page:finish', () => { Promise.race([ // possibly no head update is needed new Promise(resolve => setTimeout(resolve, 100)), @@ -39,5 +39,11 @@ export function useScriptEventPage(onChange?: (payload: TrackedPage) => void) { } }) }) + + onScopeDispose(() => { + stopDomWatcher() + stopPageFinishHook() + }) + return payload } From 1888da3d9fdd7ef83c35939b3593de46125718d7 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 24 Sep 2025 13:45:57 +1000 Subject: [PATCH 2/3] feat(matomoAnalytics)!: watch mode --- .../scripts/analytics/matomo-analytics.md | 29 +++++++++++++++---- src/runtime/registry/matomo-analytics.ts | 24 +++++++++++++-- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/docs/content/scripts/analytics/matomo-analytics.md b/docs/content/scripts/analytics/matomo-analytics.md index 4c62d3d6..23a0c021 100644 --- a/docs/content/scripts/analytics/matomo-analytics.md +++ b/docs/content/scripts/analytics/matomo-analytics.md @@ -17,7 +17,7 @@ use the [useScriptMatomoAnalytics](#useScriptMatomoAnalytics) composable. ## Loading Globally -The following config assumes you're using Matomo Cloud with the default `siteId` of `1`. +The following config assumes you're using Matomo Cloud with the default `siteId` of `1`. Page views are **automatically tracked** on navigation by default. If you're self-hosting, you'll need to provide the `matomoUrl` instead. If you have other sites you want to track, you can add them using `siteId`. @@ -84,13 +84,13 @@ const matomoAnalytics = useScriptMatomoAnalytics({ }) ``` -By default, a `siteId` of `1` is used and the page is not tracked. You can enable tracking by setting `trackPageView` to `true`. +By default, a `siteId` of `1` is used and page tracking is **automatically enabled** via the `watch` option. ```ts const matomoAnalytics = useScriptMatomoAnalytics({ cloudId: 'YOUR_CLOUD_ID', // e.g. nuxt.matomo.cloud - trackPageView: true, siteId: 2, + // watch: true, // enabled by default - automatic page tracking! }) ``` @@ -109,6 +109,23 @@ proxy._paq.push(['trackPageView']) Please see the [Config Schema](#config-schema) for all available options. +## Custom Page Tracking + +By default, all pages are tracked automatically, to disable the automatic tracking you can provide `watch: false`. + +```ts +// Manual tracking only +const { proxy } = useScriptMatomoAnalytics({ + cloudId: 'YOUR_CLOUD_ID', + watch: false, // disable automatic tracking +}) + +// Manually track page views +proxy._paq.push(['setDocumentTitle', document.title]) +proxy._paq.push(['setCustomUrl', window.location.pathname]) +proxy._paq.push(['trackPageView']) +``` + ### Using Matomo Self-Hosted For self-hosted Matomo, set `matomoUrl` to customize tracking, you may need to set the `trackerUrl` if you've customized this. @@ -151,11 +168,13 @@ You must provide the options when setting up the script for the first time. // matomoUrl and site are required export const MatomoAnalyticsOptions = object({ matomoUrl: optional(string()), - siteId: optional(string()), + siteId: optional(union([string(), number()])), + cloudId: optional(string()), trackerUrl: optional(string()), - trackPageView: optional(boolean()), + trackPageView: optional(boolean()), // deprecated - use watch instead enableLinkTracking: optional(boolean()), disableCookies: optional(boolean()), + watch: optional(boolean()), // default: true }) ``` diff --git a/src/runtime/registry/matomo-analytics.ts b/src/runtime/registry/matomo-analytics.ts index b96d3c45..0cf4f016 100644 --- a/src/runtime/registry/matomo-analytics.ts +++ b/src/runtime/registry/matomo-analytics.ts @@ -1,7 +1,9 @@ import { withBase, withHttps, withoutProtocol, withoutTrailingSlash } from 'ufo' import { useRegistryScript } from '../utils' +import { useScriptEventPage } from '../composables/useScriptEventPage' import { boolean, object, optional, string, number, union } from '#nuxt-scripts-validator' import type { RegistryScriptInput } from '#nuxt-scripts/types' +import { logger } from '../logger' export const MatomoAnalyticsOptions = object({ matomoUrl: optional(string()), @@ -11,6 +13,7 @@ export const MatomoAnalyticsOptions = object({ trackPageView: optional(boolean()), enableLinkTracking: optional(boolean()), disableCookies: optional(boolean()), + watch: optional(boolean()), }) export type MatomoAnalyticsInput = RegistryScriptInput @@ -48,6 +51,17 @@ export function useScriptMatomoAnalytics(_options? return window._paq.push(...args) }, } + + // Set up automatic page view tracking if watch is enabled (default: true) + // Skip if trackPageView is explicitly set to avoid double tracking + if (options?.watch !== false && options?.trackPageView === undefined) { + useScriptEventPage((payload) => { + _paqProxy.push(['setDocumentTitle', payload.title]) + _paqProxy.push(['setCustomUrl', payload.path]) + _paqProxy.push(['trackPageView']) + }) + } + return { _paq: _paqProxy } }, }, @@ -67,8 +81,14 @@ export function useScriptMatomoAnalytics(_options? _paq.push(['setTrackerUrl', withBase(`/matomo.php`, withHttps(normalizedCloudId))]) } _paq.push(['setSiteId', String(options?.siteId) || '1']) - if (options?.trackPageView) { - _paq.push(['trackPageView']) + // Deprecated: trackPageView option + if (options?.trackPageView !== undefined) { + if (import.meta.dev) { + logger.warn('The `trackPageView` option is deprecated. Use `watch: true` (default) for automatic page view tracking, or remove this option entirely.') + } + if (options.trackPageView) { + _paq.push(['trackPageView']) + } } }, } From 5995d14f39f7fbf50afea99dab1f3b8535904b28 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 24 Sep 2025 13:53:07 +1000 Subject: [PATCH 3/3] feat(matomoAnalytics)!: watch mode --- .../scripts/analytics/matomo-analytics.md | 22 ++++++++++++++----- src/runtime/registry/matomo-analytics.ts | 17 ++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/docs/content/scripts/analytics/matomo-analytics.md b/docs/content/scripts/analytics/matomo-analytics.md index 23a0c021..b29d3a04 100644 --- a/docs/content/scripts/analytics/matomo-analytics.md +++ b/docs/content/scripts/analytics/matomo-analytics.md @@ -114,16 +114,28 @@ Please see the [Config Schema](#config-schema) for all available options. By default, all pages are tracked automatically, to disable the automatic tracking you can provide `watch: false`. ```ts -// Manual tracking only +import { useScriptEventPage } from '#nuxt-scripts' + const { proxy } = useScriptMatomoAnalytics({ cloudId: 'YOUR_CLOUD_ID', watch: false, // disable automatic tracking }) -// Manually track page views -proxy._paq.push(['setDocumentTitle', document.title]) -proxy._paq.push(['setCustomUrl', window.location.pathname]) -proxy._paq.push(['trackPageView']) +// Custom page tracking with additional logic +useScriptEventPage((payload) => { + // Set custom dimensions based on route + if (payload.path.startsWith('/products')) { + proxy._paq.push(['setCustomDimension', 1, 'Product Page']) + } + + // Standard Matomo tracking calls (same as built-in watch behavior) + proxy._paq.push(['setDocumentTitle', payload.title]) + proxy._paq.push(['setCustomUrl', payload.path]) + proxy._paq.push(['trackPageView']) + + // Track additional custom events + proxy._paq.push(['trackEvent', 'Navigation', 'PageView', payload.path]) +}) ``` ### Using Matomo Self-Hosted diff --git a/src/runtime/registry/matomo-analytics.ts b/src/runtime/registry/matomo-analytics.ts index 0cf4f016..0a3ddebf 100644 --- a/src/runtime/registry/matomo-analytics.ts +++ b/src/runtime/registry/matomo-analytics.ts @@ -52,16 +52,6 @@ export function useScriptMatomoAnalytics(_options? }, } - // Set up automatic page view tracking if watch is enabled (default: true) - // Skip if trackPageView is explicitly set to avoid double tracking - if (options?.watch !== false && options?.trackPageView === undefined) { - useScriptEventPage((payload) => { - _paqProxy.push(['setDocumentTitle', payload.title]) - _paqProxy.push(['setCustomUrl', payload.path]) - _paqProxy.push(['trackPageView']) - }) - } - return { _paq: _paqProxy } }, }, @@ -90,6 +80,13 @@ export function useScriptMatomoAnalytics(_options? _paq.push(['trackPageView']) } } + else if (options?.watch !== false) { + useScriptEventPage((payload) => { + window._paq.push(['setDocumentTitle', payload.title]) + window._paq.push(['setCustomUrl', payload.path]) + window._paq.push(['trackPageView']) + }) + } }, } }, _options)