# Enhance Web Instant Nav

> Instant in-site nav: Speculation Rules, View Transitions, bfcache, 103 Early Hints. Use when "instant navigation", "prerender", "early hints", "back button reloads", or "second page is slow". First-load CWV → audit-performance. JS weight → audit-bundle-size. SPA → framework prefetch.

- Skill: `kensaurus/enhance-web-instant-nav` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add kensaurus/enhance-web-instant-nav`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kensaurus/enhance-web-instant-nav/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: MIT
- Author: kensaurus (https://skillmd.com/u/kensaurus)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/kensaurus/enhance-web-instant-nav

---


> Surface router: `/uiux`. You are here: `enhance-web-instant-nav`.

# enhance-web-instant-nav — Instant Navigations

**Degree of freedom: MIXED.** Which pages to speculate `[HIGH freedom]`;
the exclusion list, `Sec-Purpose` handling, and re-measure `[LOW freedom — run exactly]`.

> Applies to MPAs and Next.js App Router (server-rendered routes). Pure SPA
> routing → use the framework's prefetch API; Speculation Rules are per-document.

## How to reason

1. **Observe** — quote 2nd-page LCP p75, bfcache test result, and the top 5 next-click paths
2. **Rank** — probability × page cost. Prefetch broadly; prerender only ≥ ~50% likely next pages
3. **Guard** — list every URL with GET side effects or per-user state; exclude or downgrade to prefetch
4. **Ship** — rules + server guard + analytics guard in the same PR
5. **Re-measure** — if 2nd-page LCP didn't drop, the rule isn't firing (DevTools → Application → Speculative loads)

## Worked example

> **Observe:** blog; first-page LCP 1.9 s, next-article LCP 2.8 s; back button reloads (`unload` in analytics).
> **Rank:** "next article" 61% of sessions → prerender moderate; category links → prefetch moderate.
> **Guard:** `/logout`, `/account/*`, `?utm_*` excluded; server returns early on `Sec-Purpose`.
> **Ship:** rules block + `pagehide` swap + `document.prerendering` guard in the GA loader.
> **Re-measure:** next-article LCP 0.3 s (activation), bfcache green, no analytics double-count.

## Safety contract  [LOW freedom — run exactly]

- Never prerender: logout, cart/checkout, payment, auth, anything whose GET mutates state
- Server: if `Sec-Purpose` contains `prefetch`, skip counters/side effects; return normal HTML
- Client: analytics, ads, consent, A/B run only after `document.prerendering === false` or on `prerenderingchange`
- Never add `unload` listeners; never set `Cache-Control: no-store` on HTML unless legally required
- Cross-origin / cross-site prerender is not attempted

## Phase 1 — Measure  [LOW freedom — run exactly]

- `web-vitals/attribution` `onLCP` on the *second* page in a session; record p75
- DevTools → Application → Back/forward cache → Test; record blocking reasons
- `curl -sv <url> 2>&1 | grep "< HTTP"` — is a `103` emitted today?
- Top next-click paths from analytics (or infer from IA)

```bash
# Field: CrUX / PSI p75 for origin + top URLs
npx @lhci/cli autorun --config=lighthouserc.json
curl -sv https://example.com/ 2>&1 | grep -E "< HTTP/.* 103"
```

## Phase 2 — Speculation Rules  [HIGH freedom on selection]

Baseline (adjust `where` to the site's IA):

```html
<script type="speculationrules">
{
  "prefetch": [{
    "where": { "and": [
      { "href_matches": "/*" },
      { "not": { "href_matches": ["/logout", "/account/*", "/cart*", "/checkout*", "/api/*"] } },
      { "not": { "selector_matches": "[rel~=nofollow], .no-prefetch" } }
    ]},
    "eagerness": "moderate"
  }],
  "prerender": [{
    "where": { "href_matches": "/posts/*" },
    "eagerness": "moderate"
  }]
}
</script>
```

- Eagerness: `conservative` (pointerdown) → `moderate` (hover ~200 ms) → `eager` → `immediate` (list rules only). Default `moderate`.
- Query strings: `"expects_no_vary_search": "params=(\"utm_source\" \"utm_medium\")"` or exclude `?` URLs
- Next.js App Router: keep `<Link>` prefetch for RSC payloads; add the block in `app/layout.tsx` via a raw `<script>`. On Next.js 16.3+ evaluate built-in **Instant Navigations** first; if it covers the case, skip prerender rules and keep only prefetch.
- Chromium-first progressive enhancement. Check caniuse at implementation time — never block on Firefox/Safari.

Syntax depth: `references/speculation-rules.md`.

## Phase 3 — bfcache  [LOW freedom — run exactly]

- Replace `unload` → `pagehide`; `beforeunload` only while unsaved input exists
- Close WebSockets / BroadcastChannel in `pagehide`; reopen on `pageshow` if `event.persisted`
- Drop `Cache-Control: no-store` from HTML (keep it on APIs)
- Re-run the DevTools test until green

Blocker list: `references/bfcache-blockers.md`.

## Phase 4 — View Transitions (cross-document)  [HIGH freedom]

```css
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) { @view-transition { navigation: none; } }
.hero-img { view-transition-name: hero; }
```

- Pair with prerender: activation + transition = app-like feel
- Progressive: nothing breaks without it
- Keep `view-transition-name` unique per document or the transition aborts
- `prefers-reduced-motion` is required (pairs with `audit-accessibility`)

## Phase 5 — 103 Early Hints  [LOW freedom — run exactly]

- HTML responses only: `Link: </hero.avif>; rel=preload; as=image; fetchpriority=high`, `Link: <https://cdn.example>; rel=preconnect`
- Node/Next custom server: `res.writeEarlyHints({ link: [...] })`; else CDN config
- Do not hint > ~5 resources; do not hint per-user content
- Per-platform: `references/early-hints.md`

## Self-critique before reporting

- **Exclusions listed** — every GET-with-side-effects URL is in the PR
- **Server checks `Sec-Purpose`** — grep it
- **No double analytics** — prerender → activation tested
- **Prerender scarce** — only pages with ≥ ~50% next-click probability
- **Re-measured** — 2nd-page LCP p75 dropped, or the rule isn't firing
- **Right owner** — first-load LCP/INP/CLS → `audit-performance`; JS weight → `audit-bundle-size`

## Required output

1. Before/after: 2nd-page LCP p75, bfcache status, 103 present, transition on/off
2. Rules block + exclusion list
3. Server guard + client analytics guard diffs
4. One-line rollback: delete the `<script type="speculationrules">` block

## Related

- `audit-performance` — first-load CWV / loading priority
- `audit-bundle-size` — JS weight; don't double-prefetch
- `audit-accessibility` — reduced-motion for View Transitions
- `enhance-pwa` — service worker vs speculation / bfcache

