Skip to main content
ariefrse.my
HomeSkillsProjectsAI WorkWritingContact
Resume
All writing

Note · Next.js

The menu link that did nothing when clicked

A shared "render everything through Link" abstraction is a trap the moment one item is off-origin. The click is intercepted and then nothing happens at all.

July 6, 2026 · 5 min read

Another entry in the bugs that do not crash family.

In the App Router, wrapping a cross-origin URL in next/link can fail without a trace. The click is intercepted, the client router tries to resolve a route it does not own, and then nothing happens.

Symptom
The sidebar item lights up as active. The content area goes blank. No navigation, no console error, no 404.
Cause
next/link intercepted the click for a cross-origin href the client router cannot resolve.
Fix
Branch on the href shape and render external links as a plain anchor.

How it got in

The sidebar renders nav items from a config array. Adding an external item felt free:

// navigation.ts
{ id: 'notes', label: 'Notes', href: 'https://ariefrsee.github.io/notes/' }

Every item went through the same <Link>. Internal routes worked, so the pattern looked proven. Nobody clicked the one external item before shipping, because why would you click the nav item you just added to a list of nav items that already work.

The fix

- <Link href={item.href}>{item.label}</Link>
+ {isExternalHref(item.href) ? (
+   <a href={item.href} target="_blank" rel="noopener noreferrer">{item.label}</a>
+ ) : (
+   <Link href={item.href}>{item.label}</Link>
+ )}

Two follow-on details are worth encoding rather than rediscovering.

Mobile drawers close on route change. An external link opening a new tab never changes pathname, so the drawer stays open behind it. Wire onClick to the close handler on the anchor explicitly.

Mark it visually. An outbound-arrow icon in the badge slot tells people they are leaving the site before they click, not after.

What I learned

A shared "render everything through <Link>" abstraction is a trap the moment one item is off-origin. The abstraction is not wrong, it is just silently partial.

Click every nav item in a real browser. The ones that fail silently are exactly the ones a build can never catch for you.

nextjsapp-routernavigation

Share

XLinkedInWhatsAppThreads

Related

  • Everything fails silentlyGreen is not evidence. It is the absence of evidence. A running gallery of production failures that passed every check that existed.
  • Upgrading a website's engine: the bugs that don't crashEvery project page on my portfolio rendered a 404 for months while returning HTTP 200. On this bug class, "it builds and the page loads" is what failure looks like.

© 2026 Muhammad Arief Roseli

GitHubLinkedInSnapInvois