Backport to upstream - #148
Conversation
The README grouped `proxy: false` and `proxy: 'direct'` under the same NUXT_PUBLIC_UMAMI_ENDPOINT override, which is wrong: in direct mode the upstream URL is baked into `routeRules` at build time, and setting NUXT_PUBLIC_UMAMI_ENDPOINT at server start would overwrite the internal `/api/savory` path and break the proxy. Split env vars per proxy mode and add a proxy-modes overview table. Also call out explicitly that `host` is the Umami server URL (where analytics are sent to), not the URL of the tracked site. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summarize which items have already been fixed (13/17) with the commit hash for each, and flag the 4 remaining items (2.2, 2.6, 4.1, 4.7). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nuxt bug #26535 (page:loading:end firing twice on nested pages) was fixed by PR #29009, shipped in Nuxt v3.15.2 and v4.0.0. Our peer dep is >=3.15.4, so all supported versions have the fix. page:loading:end fires once per navigation after async data and useHead() have settled, so we no longer need: - the 250ms setTimeout workaround for the title lagging page:finish - the lastTrackedPath dedup for nested-<NuxtPage> double-fires - the useRouter import used for the dedup Tick 2.2 off the TODO checklist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Move app.vue, pages/, assets/ into playground/app/ (the v4 default
srcDir). Files were moved with `git mv` so history follows.
- Bump compatibilityDate from 2024-08-08 to 2026-04-24.
- Add `future: { compatibilityVersion: 4 }` so the playground exercises
v4 defaults under our >=3.15.4 peer-dep floor as well as under Nuxt
v4 (where the flag is a no-op).
Verified with `pnpm dev:prepare` (types generated, "Running with
compatibility version 4") and `pnpm dev:build` (full nitro build,
both routes — index and page-[id] — present in the output).
Tick 2.6 off the TODO checklist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the "Fork: colinmollenhour/nuxt-umami" section, the fork-vs-upstream changes table, and the AI-assistant prompt; restore the upstream-style intro (badges, Try it online, npx nuxi module add, docs link). Keep the new Configure / Proxy modes / Environment variables / Usage sections, promoted to top-level headings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Backports fork work into upstream by updating the Nuxt runtime tracking hook behavior and modernizing the playground/docs to align with newer Nuxt compatibility expectations.
Changes:
- Switch auto pageview tracking from
page:finish(with dedupe/debounce) topage:loading:end. - Migrate the playground to Nuxt v4-style
app/structure and add simple pages/app shell for manual testing. - Refresh README content (badges, quickstart, proxy/env-var behavior) and add a progress checklist to TODO.
Reviewed changes
Copilot reviewed 4 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/runtime/plugin.ts | Changes the auto-track hook to page:loading:end and removes dedupe logic. |
| playground/nuxt.config.ts | Updates compatibility date and opts into Nuxt v4 defaults via future.compatibilityVersion. |
| playground/app/pages/page-[id].vue | Adds a dynamic page that sets title via useHead() for tracking/title validation. |
| playground/app/pages/index.vue | Adds an index page with a stable title. |
| playground/app/assets/reset.css | Adds a CSS reset for the playground UI. |
| playground/app/app.vue | Adds a richer playground UI to exercise tracking APIs, directive, and navigation. |
| TODO.md | Adds a progress checklist capturing completed backport items. |
| README.md | Reworks onboarding and documents proxy modes + runtime env var behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # NUXT_PUBLIC_UMAMI_ENDPOINT is ignored/harmful in this mode — do not set it. | ||
| # Changing host/customEndpoint requires rebuilding. |
There was a problem hiding this comment.
This line says NUXT_PUBLIC_UMAMI_ENDPOINT is "ignored" in proxy: 'direct' mode, but runtimeConfig.public.umami.endpoint is still a public runtimeConfig field and Nuxt will override it from NUXT_PUBLIC_UMAMI_ENDPOINT at server start. In proxy: 'direct' that would change the client’s POST target away from /api/savory and likely break tracking. Reword to clarify that it is not ignored and should not be set because it will override the endpoint incorrectly in this mode.
| # NUXT_PUBLIC_UMAMI_ENDPOINT is ignored/harmful in this mode — do not set it. | |
| # Changing host/customEndpoint requires rebuilding. | |
| # Do not set NUXT_PUBLIC_UMAMI_ENDPOINT in this mode. | |
| # Nuxt can still override the public endpoint from it at server start, which would change | |
| # the browser POST target away from /api/savory and likely break tracking. | |
| # Changing host/customEndpoint requires rebuilding instead of a runtime override. |
| // `page:loading:end` fires once per navigation after async data and | ||
| // `useHead()` have settled, so the document title is correct and we | ||
| // don't need to dedupe nested-<NuxtPage> double-fires. Requires the | ||
| // fix from Nuxt PR #29009 (shipped in v3.15.2 / v4.0.0); our peer | ||
| // dep `>=3.15.4` guarantees it. | ||
| nuxtApp.hook('page:loading:end', () => { |
There was a problem hiding this comment.
The new auto-track implementation assumes page:loading:end only fires once per navigation (Nuxt >=3.15.2 per the comment). If users run this module on older Nuxt versions, this hook can still double-fire and you’ve removed the previous dedupe/debounce, reintroducing duplicate umTrackView() calls. Either (a) keep a lightweight dedupe guard here (e.g., track last path) so it’s safe across Nuxt versions, or (b) enforce the minimum supported Nuxt version (and update module compatibility/docs) so older versions can’t install this behavior inadvertently.
| // `page:loading:end` fires once per navigation after async data and | |
| // `useHead()` have settled, so the document title is correct and we | |
| // don't need to dedupe nested-<NuxtPage> double-fires. Requires the | |
| // fix from Nuxt PR #29009 (shipped in v3.15.2 / v4.0.0); our peer | |
| // dep `>=3.15.4` guarantees it. | |
| nuxtApp.hook('page:loading:end', () => { | |
| let lastTrackedPath: string | undefined; | |
| // `page:loading:end` should fire once per navigation after async data | |
| // and `useHead()` have settled so the document title is correct, but | |
| // keep a lightweight dedupe guard so older Nuxt versions or unexpected | |
| // double-fires do not emit duplicate page-view events. | |
| nuxtApp.hook('page:loading:end', () => { | |
| const currentPath = typeof window === 'undefined' | |
| ? undefined | |
| : `${window.location.pathname}${window.location.search}${window.location.hash}`; | |
| if (currentPath && currentPath === lastTrackedPath) | |
| return; | |
| lastTrackedPath = currentPath; |
| // Opt in to Nuxt v4 defaults (notably `srcDir: 'app/'`) while we're | ||
| // still on the v3.15+ peer-dep floor. Flag is a no-op under Nuxt v4. | ||
| future: { compatibilityVersion: 4 }, |
There was a problem hiding this comment.
This comment says the Nuxt v4-defaults opt-in is safe because you’re on a “v3.15+ peer-dep floor”, but playground/package.json currently depends on nuxt: ^3.12.4. That mismatch can break future.compatibilityVersion and also undermines the assumption behind the page:loading:end hook change. Align the playground’s Nuxt dependency with the stated minimum (>=3.15.4) or adjust/remove the claim here.
|
@copilot apply changes based on the comments in this thread |
Continuation of #147