From 64a04c400dcf70eecdf654c7a24b71c34ed6960e Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 15 Apr 2026 10:50:45 +0200 Subject: [PATCH] fix(blog,site): prevent UTM click handler from breaking cross-app navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit router.push() only works for pages owned by the current Next.js app. The UTM persistence click handler was using it for every same-origin link, which broke navigation to pages served by other apps: - Site app: /docs and /blog are proxied via rewrites to separate origins. router.push() tried client-side routing to a page that doesn't exist in the site app — click did nothing. - Blog app: has basePath "/blog", so router.push("/pricing") became /blog/pricing — 404. Now each app only uses router.push() for paths it owns. Cross-app links fall through to anchor.setAttribute(), letting the browser do a full navigation where server rewrites apply correctly. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/blog/src/components/utm-persistence.tsx | 27 ++++++++++++++------ apps/site/src/components/utm-persistence.tsx | 15 ++++++++++- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/apps/blog/src/components/utm-persistence.tsx b/apps/blog/src/components/utm-persistence.tsx index 2ba8c3b377..d9c041b659 100644 --- a/apps/blog/src/components/utm-persistence.tsx +++ b/apps/blog/src/components/utm-persistence.tsx @@ -76,18 +76,29 @@ export function UtmPersistence() { } const nextHref = `${targetUrl.pathname}${targetUrl.search}${targetUrl.hash}`; - const internalPathname = - targetUrl.pathname === BLOG_PREFIX - ? "/" - : targetUrl.pathname.replace(new RegExp(`^${BLOG_PREFIX}(?:/|$)`), "/"); - const nextInternalHref = - `${internalPathname}${targetUrl.search}${targetUrl.hash}`; + const isBlogPath = + targetUrl.pathname === BLOG_PREFIX || + targetUrl.pathname.startsWith(`${BLOG_PREFIX}/`); const isModifiedClick = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey; - if (isInternalLink && anchor.target !== "_blank" && !isModifiedClick) { + if ( + isInternalLink && + isBlogPath && + anchor.target !== "_blank" && + !isModifiedClick + ) { + const internalPathname = + targetUrl.pathname === BLOG_PREFIX + ? "/" + : targetUrl.pathname.replace( + new RegExp(`^${BLOG_PREFIX}(?:/|$)`), + "/", + ); event.preventDefault(); - router.push(nextInternalHref); + router.push( + `${internalPathname}${targetUrl.search}${targetUrl.hash}`, + ); return; } diff --git a/apps/site/src/components/utm-persistence.tsx b/apps/site/src/components/utm-persistence.tsx index d0c1c90c76..31c204b494 100644 --- a/apps/site/src/components/utm-persistence.tsx +++ b/apps/site/src/components/utm-persistence.tsx @@ -80,7 +80,20 @@ export function UtmPersistence() { const isModifiedClick = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey; - if (isInternalLink && anchor.target !== "_blank" && !isModifiedClick) { + // Paths proxied to other apps via rewrites — must use full navigation + // so the server-side rewrite kicks in instead of client-side routing. + const isProxiedPath = + targetUrl.pathname === "/docs" || + targetUrl.pathname.startsWith("/docs/") || + targetUrl.pathname === "/blog" || + targetUrl.pathname.startsWith("/blog/"); + + if ( + isInternalLink && + !isProxiedPath && + anchor.target !== "_blank" && + !isModifiedClick + ) { event.preventDefault(); router.push(nextHref); return;