Prefetching
pracht prefetches route data before navigation so page transitions feel instant. Prefetching is automatic for server-rendered routes and can be configured per route.
How It Works
After hydration, pracht sets up document-level listeners that watch for user interaction with internal links. When a prefetch is triggered, the route's server data (the same JSON payload used during client-side navigation) is fetched in the background and cached. When the user actually clicks the link, the cached data is used immediately โ no second network request.
Prefetched data is held in a 30-second TTL cache. Stale entries are discarded and re-fetched on the next interaction.
Strategies
Each route can declare a prefetch strategy in its route meta:
| Strategy | Trigger | Best For |
|---|---|---|
"intent" |
Mouse hover or keyboard focus (50ms debounce) | Most routes โ low overhead, high hit rate |
"viewport" |
Link scrolls into view (IntersectionObserver) | Navigation menus, link-heavy pages |
"hover" |
Same as intent (hover + focus) | Alias for intent |
"none" |
Disabled | SPA routes, rarely visited pages |
Defaults
You don't need to configure anything for most apps. The defaults are:
- SSR, SSG, ISG routes โ
"intent"(prefetch on hover/focus) - SPA routes โ
"none"(SPA routes already fetch data on load, so prefetching the server payload has no benefit)
Per-Route Configuration
Override the default strategy with the prefetch field on a route:
import { defineApp, route, group } from "@pracht/core";
export const app = defineApp({
routes: [
// Prefetch when the link enters the viewport
route("/pricing", "./routes/pricing.tsx", {
render: "isg",
prefetch: "viewport",
}),
// Disable prefetching for a rarely visited page
route("/terms", "./routes/terms.tsx", {
render: "ssg",
prefetch: "none",
}),
// Default: intent-based prefetching (hover/focus)
route("/about", "./routes/about.tsx", { render: "ssg" }),
],
});Viewport Prefetching
When a route uses "viewport", pracht observes all <a> elements pointing to that route via an IntersectionObserver with a 200px root margin. As soon as the link scrolls near the viewport, the route data is prefetched. Each link is only observed once โ after the first intersection, it is unobserved to avoid redundant work.
After client-side navigation updates the DOM, a MutationObserver re-scans for new viewport-prefetch links automatically.
Cache Behavior
- Prefetch results are cached for 30 seconds. After that, the entry is evicted and re-fetched on the next trigger.
- The cache is keyed by URL (pathname + search). Different query parameters are cached separately.
- If a prefetch is in flight when the user clicks the link, the in-flight promise is reused โ no duplicate request.
- The cache is shared across all prefetch strategies. A viewport prefetch can be consumed by a subsequent click, and vice versa.