Skip to content

fix: record client navigations in apps without pages - #235

Merged
sadjow merged 3 commits into
mainfrom
fix/route-change-without-pages
Oct 15, 2026
Merged

sadjow merged 3 commits into
mainfrom
fix/route-change-without-pages

Conversation

@sadjow

@sadjow sadjow commented Oct 15, 2026

Copy link
Copy Markdown
Member

With trackOnRouteChange: true, an app without a pages/ directory recorded only the landing visit. Later client-side navigations to new campaign URLs were never captured.

Without pages, Nuxt uses its minimal router. It keeps one reactive route object and updates it with Object.assign before it calls the afterEach guards, and the from argument is that same object. By the time the plugin's guard ran, from already matched to, so the to.fullPath !== from.fullPath check skipped every navigation.

The plugin now compares each navigation with the last full path it saw, starting from the route it records on mount. The starting point is the visited route the landing capture already uses. While a prerendered page hydrates, the live route is still the prerendered path without its query, and Nuxt then replays the visited URL with router.replace. Starting from the live route would treat that replay as a navigation and run utm:before-track a second time for the landing visit.

Release note

Apps without a pages/ directory that enable trackOnRouteChange now record client-side navigations, as the option describes. Apps with pages record the same navigations as before.

Validation

A browser test in an app without pages lands on /?utm_source=landing, navigates to /?utm_source=newsletter, and expects the newsletter visit as the last touch, with both visits in history. On main it fails with expected 'Last touch: landing' to be 'Last touch: newsletter', and with this change it passes. A second browser test, in a pages app, lands on a prerendered page with async setup, follows a link to a new campaign, and expects utm:before-track to run once for each. It passes on main and with this change, and fails with three calls when the comparison starts from the live route. Built against Nuxt 3.21.11, the same app without pages behaves the same way: main's plugin keeps landing as the last touch and stores one visit, while this change stores both. pnpm test (153 passed, 1 skipped), lint, format, and the root and playground type checks pass locally.

@sadjow
sadjow merged commit 2775205 into main Oct 15, 2026
8 checks passed
@sadjow
sadjow deleted the fix/route-change-without-pages branch October 15, 2026 01:58
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