---
title: "Navigation Performance"
description: "Optimize cross-app navigation with prefetching and prerendering using Speculation Rules."
canonical_url: "https://vercel.com/academy/microfrontends-on-vercel/navigation-performance"
md_url: "https://vercel.com/academy/microfrontends-on-vercel/navigation-performance.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-11T12:18:09.398Z"
content_type: "lesson"
course: "microfrontends-on-vercel"
course_title: "Microfrontends on Vercel"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Navigation Performance

# Navigation Performance

Cross-app navigation means full page loads. Speculation Rules make those navigations feel instant by prefetching before users click.

## Outcome

Implement Speculation Rules in the shared Header to prefetch and prerender cross-app pages before users click.

## The Problem: Hard Navigations

In a single-page app, clicking a link triggers a client-side route change. It's fast and seamless. In microfrontends:

```
Click "Docs" in marketing app
└── Full page load to docs app
    └── HTML request
    └── JavaScript download
    └── React hydration
    └── Visible content
```

This takes longer than a client-side transition. Users notice.

## The Solution: Speculation Rules

[Speculation Rules](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API) tell the browser to prefetch or prerender pages before users navigate:

- **Prefetch** - Download the HTML and critical resources
- **Prerender** - Fully render the page in a hidden tab

When the user clicks, the page is already ready.

Speculation Rules only work in Chrome 109+ and Edge 109+. Safari and Firefox ignore the script. No error, just no prefetch optimization for \~25% of users.

## Fast Track

1. Add Speculation Rules script to the shared Header
2. Configure prefetch for moderate eagerness
3. Configure prerender for conservative eagerness

## Hands-on Exercise 3.1

Add Speculation Rules to the shared Header component.

### Part 1: Create the Speculation Rules Component

Create `packages/ui/src/speculation-rules.tsx`:

```tsx title="packages/ui/src/speculation-rules.tsx"
export function SpeculationRules() {
  const rules = {
    prefetch: [
      {
        source: "document",
        where: {
          and: [
            { href_matches: "/*" },
            { not: { href_matches: "/api/*" } },
            { not: { href_matches: "/_next/*" } },
          ],
        },
        eagerness: "moderate",
      },
    ],
    prerender: [
      {
        source: "document",
        where: {
          and: [
            { href_matches: "/*" },
            { not: { href_matches: "/api/*" } },
            { not: { href_matches: "/_next/*" } },
          ],
        },
        eagerness: "conservative",
      },
    ],
  };

  return (
    <script
      type="speculationrules"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(rules) }}
    />
  );
}
```

`moderate` eagerness prefetches on hover. `conservative` only prerenders when the click starts (mousedown).

### Part 2: Add to Header

Update `packages/ui/src/header.tsx`:

```tsx title="packages/ui/src/header.tsx" {1,6}
import { SpeculationRules } from "./speculation-rules";

export function Header() {
  return (
    <>
      <SpeculationRules />
      <header className="h-16 border-b bg-white px-6 flex items-center justify-between">
        {/* ... existing header content ... */}
      </header>
    </>
  );
}
```

### Part 3: Export the Component

Update `packages/ui/src/index.ts`:

```ts title="packages/ui/src/index.ts"
export { Header } from "./header";
export { Footer } from "./footer";
export { SpeculationRules } from "./speculation-rules";
```

## Understanding Eagerness Levels

| Level          | When It Triggers         | Use Case                          |
| -------------- | ------------------------ | --------------------------------- |
| `immediate`    | Page load                | Critical paths users always visit |
| `eager`        | Link appears in viewport | High-traffic links                |
| `moderate`     | Hover or focus on link   | Most navigation links             |
| `conservative` | Click starts (mousedown) | Resource-heavy pages              |

For cross-app navigation:

- **Prefetch: moderate** - Download resources when user shows intent
- **Prerender: conservative** - Only fully render when click is certain

This balances performance gains against resource usage.

## Try It

1. Run `pnpm dev`
2. Open DevTools → Network
3. Visit `localhost:3024` (Marketing)
4. Hover over "Docs" in the header
5. Watch the network tab - you should see prefetch requests for `/docs`
6. Click "Docs" - the navigation should feel faster

### Measuring the Improvement

Without Speculation Rules:

```
Click → DNS → Connection → HTML → JS → Hydration → Interactive
       ~50ms    ~50ms     ~100ms  ~200ms  ~150ms
       Total: ~550ms
```

With prefetch:

```
Hover → Prefetch (background)
Click → HTML (cached) → JS → Hydration → Interactive
        ~0ms          ~200ms  ~150ms
        Total: ~350ms (36% faster)
```

With prerender:

```
Click-start → Prerender (background)
Click-end → Swap (instant)
            Total: ~50ms (90% faster)
```

## Commit

```bash
git add -A
git commit -m "perf: add Speculation Rules for cross-app prefetching"
```

## Done-When

- [ ] SpeculationRules component created
- [ ] Header includes Speculation Rules script
- [ ] Prefetch triggers on hover (moderate)
- [ ] Network tab shows prefetch requests on hover

## Advanced: Targeted Speculation

For high-traffic pages, increase eagerness:

```tsx
const rules = {
  prefetch: [
    // High-traffic: prefetch eagerly
    {
      source: "document",
      where: { href_matches: ["/docs", "/pricing"] },
      eagerness: "eager",
    },
    // Everything else: prefetch on hover
    {
      source: "document",
      where: { href_matches: "/*" },
      eagerness: "moderate",
    },
  ],
};
```

## What's Next

Write tests that catch routing misconfigurations before they reach production.


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
