Skip to content

fix(web): legal links are plain anchors, never Next-prefetched routes - #1054

Open
jiashuoz wants to merge 1 commit into
mainfrom
fix/legal-links-plain-anchor
Open

jiashuoz wants to merge 1 commit into
mainfrom
fix/legal-links-plain-anchor

Conversation

@jiashuoz

Copy link
Copy Markdown
Member

Summary

Observed on prod after v1.12.1: the Privacy/Terms links added in #1051 render same-origin paths with next/link's <Link>, which prefetches the route's RSC payload. On the hosted deployment /privacy is not a Next route — Caddy serves a static page at that path — so every landing-page load logged a 404 for /privacy/__next._tree.txt?_rsc=….

Legal URLs are by definition operator-supplied paths that may live outside the Next app (the hosted deployment's legal pages come from the private ops repo via a Caddy route), so they must never be prefetched or client-routed by Next.

Rule going forward: legal links always render with a plain <a> — same-origin paths open in the same tab (no target), absolute cross-origin URLs keep target="_blank" rel="noopener noreferrer". Never next/link's <Link>, even when the href looks same-origin.

Changes

Touches exactly the three call sites from #1051:

  • web/src/app/components/SignInConsent.tsx (LegalAnchor) — always renders <a> instead of choosing <Link> for same-origin paths.
  • web/src/app/components/LegalFooterLinks.tsx — always renders <a>, since every entry it receives comes from legalFooterLinks() and is plain by definition.
  • web/src/app/page.tsx — added a plain?: boolean field to the FOOTER_LINKS type; entries from ...legalFooterLinks() render as a bare <a>, every other footer entry (GitHub, SDKs, blog, etc.) is untouched and still uses <Link>/<a> exactly as before.
  • web/src/lib/site.ts — LegalLink now carries plain: true, with an updated comment on legalFooterLinks() explaining why (operator-supplied paths that may resolve outside this Next app's own routing).

Tests

Updated the four existing hosted-build tests (page.legal-links.hosted.test.tsx, SignInConsent.test.tsx, LegalFooterLinks.test.tsx, (app)/layout.legal-consent.hosted.test.tsx) plus lib/site.test.ts. Each file's next/link mock now tags anything that renders through <Link> with a data-next-link marker attribute, and a new/extended assertion checks the marker is absent on the Privacy/Terms links. Verified each of the four hosted tests fails against the pre-fix source and passes after.

Test plan

  • npm ci
  • npm run lint
  • npx tsc --noEmit
  • npm test — 1055/1055 passing
  • npm run build
  • Confirmed the four updated hosted tests fail pre-fix (stashed source changes, kept test changes) and pass post-fix

🤖 Generated with Claude Code

Privacy/Terms links from #1051 render same-origin paths with next/link's
<Link>, which prefetches the route's RSC payload. On the hosted deployment
/privacy and /terms are not Next routes — Caddy serves static pages at
those paths — so every landing-page load logged a 404 for the prefetch
request.

Legal URLs are operator-supplied and may resolve outside this Next app by
design, so they must never be prefetched or client-routed. Render them
with a plain <a> always: same-origin paths open in the same tab, absolute
cross-origin URLs keep target="_blank" rel="noopener noreferrer".

Touches the three call sites from #1051 (SignInConsent's LegalAnchor,
LegalFooterLinks, and the landing page footer's FOOTER_LINKS) plus a new
`plain` field on the link type legalFooterLinks() returns, so the footer
map can render those entries as bare anchors without touching any other
footer link. Existing hosted tests are updated to assert a plain anchor
via a next/link mock marker, each made to fail before this fix.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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