Skip to content

fix: capture the router's current route when pages call capture during navigation - #221

Merged
sadjow merged 3 commits into
mainfrom
fix/capture-current-route
Oct 14, 2026
Merged

sadjow merged 3 commits into
mainfrom
fix/capture-current-route

Conversation

@sadjow

@sadjow sadjow commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

capture() defaulted to nuxtApp._route. With the pages router, Nuxt syncs _route only after the new page's Suspense resolves, so a page that called useNuxtUTM().capture() from setup or onMounted after a client-side navigation recorded the previous route instead of its own. Navigating from / to /landing?utm_source=newsletter and capturing on mount stored / again, and since that matched the existing entry for the session, deduplication dropped it and the newsletter campaign was never recorded.

capture() now defaults to the router's current route, which vue-router updates before the new page renders. It reads it with unref(router.currentRoute) because apps without the pages router use Nuxt's minimal router, whose currentRoute is a computed over the route it assigns to _route from Nuxt 3.9.1 onward but the reactive route object itself in 3.0.0 through 3.9.0. Reading .value there would throw on mount, and the module supports Nuxt from 3.0.0. Route-change tracking already passed the destination route explicitly, so it behaves as before.

The new test/fixtures/pages fixture has a home page linking to /landing?utm_source=newsletter and a landing page that calls capture() on mount. Its browser test fails without the fix (expected undefined to be 'newsletter') and passes with it. The runtime plugin unit tests now set the mocked router's current route as a ref instead of _route, and a new case passes a plain route object the way the minimal router before Nuxt 3.9.1 does. That case failed with .value (Cannot read properties of undefined (reading 'query')) and passes with unref. The new files pass both the current ESLint config and the Prettier-only config from #220.

Validation: pnpm format:check on the changed files, pnpm lint --max-warnings 0, pnpm test:types, pnpm dev:build, pnpm test (83 tests), pnpm test:package, and pnpm test:compatibility against Nuxt 4 and Nuxt 3 pass.

@sadjow

sadjow commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

Review notes:

  • Nuxt's minimal router, used when an app has no pages/ directory, exposes currentRoute as a computed only from 3.9.1. In 3.0.0 through 3.9.0 it is the reactive route object itself, so router.currentRoute.value was undefined and capture() would throw on mount, while the module declares support from 3.0.0. ffe05a1 reads it with unref, and a new unit test passes a plain route object the way that router does. It failed with .value and passes now.
  • The route, query, and landing URL are all read before the first await, so a navigation while a hook is pending can't change what gets recorded, and aborted or redirected navigations never reach currentRoute. The new browser test fails on main with expected undefined to be 'newsletter'.

Two problems turned up while tracing this. Both already exist on main and aren't caused by this PR:

  • On prerendered pages (nuxi generate) whose setup awaits data, the first capture runs before Nuxt restores the real query string, so a visit to /campaign?utm_source=async is stored as /campaign without the campaign. Route-change tracking doesn't recover it, because the path and query look unchanged when Nuxt restores them. Reproduced with a prerendered fixture page that awaits useAsyncData.
  • Without pages/, trackOnRouteChange never fires. Reading the minimal router in Nuxt 4.5.2, it updates the shared route object before calling afterEach, so from.fullPath already equals to.fullPath.

…oute

# Conflicts:
#	test/runtime-plugin.test.ts
@sadjow
sadjow merged commit cdb42e7 into main Oct 14, 2026
7 checks passed
@sadjow
sadjow deleted the fix/capture-current-route branch October 14, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant