Client Navigation & Transitions
Master Dinou's client-side SPA navigation: smooth soft transitions, automatic hover prefetching, the Client Router Cache lifecycle, cache busting with the fresh prop, imperative useRouter controls, and Directory-First relative routing.
Looking for Type-Safe Navigation?
Dinou generates automatic route types so <Link href="..."> and router.push("...") provide zero-runtime autocomplete and compile-time error checking.
1. Navigation Overview
In Dinou, navigating between routes does not trigger a full browser refresh. Instead, Dinou operates as a high-performance single-page application (SPA):
2. The <Link> Component & Prefetching
The primary way to navigate between pages is the <Link> component imported from "dinou". It intercepts click events, prevents hard reloads, and handles background prefetching:
import { Link } from "dinou";
export function NavigationBar() {
return (
<nav className="flex gap-4">
{/* ✅ Type-Safe href: autocompleted in your IDE via DinouRoute */}
<Link href="/dashboard">Dashboard</Link>
{/* ✅ Dynamic interpolation typed against registered routes */}
<Link href={`/blog/${post.slug}`}>Post</Link>
{/* Disable prefetch for heavy routes or rarely visited pages */}
<Link href="/analytics" prefetch={false}>
Analytics
</Link>
{/* ❌ TypeScript compile error on invalid/dead links when using type-safe routing */}
{/* <Link href="/broken-link">Broken</Link> */}
</nav>
);
}Native HTML <a> Anchor Elements
<a> tags also trigger client-side soft navigation via global event delegation in Dinou. However, they lack smart features like hover prefetching and the fresh prop provided by the <Link> component.4. Client Router Cache & The fresh Prop
To deliver instant page transitions without unnecessary server requests, Dinou features an in-memory Client Router Cache. Understanding how this cache operates is key to knowing when and why to use the fresh prop.
4.1. How the Client Router Cache Works
During client execution, Dinou maintains three dedicated in-memory caches in the browser tab:
pathname + search.popstate).- On Hover: When the mouse enters a
<Link href="/pricing">, Dinou automatically prefetches the route payload in the background and stores the pending RSC promise directly intopageCache. - On Click: The router initiates navigation to
"/pricing". Because the payload was already prefetched intopageCache, the transition renders instantly with 0ms network latency. - On Back / Forward: Browser
popstateevents read frompageCacheand restore the previous scroll position fromscrollCachewithout making any network requests.
4.2. Cache Invalidation with the fresh Prop
While caching provides instantaneous navigation, pages with volatile data or pages visited immediately after a mutation (e.g., checkout, profile updates, or inventory counters) need fresh server state. The fresh prop provides explicit, predictable cache busting:
What happens under the hood when fresh is true:
Hovering over a <Link fresh> does not prefetch potentially stale data before the user actually clicks.
Upon navigation, the router explicitly runs pageCache.delete(targetUrl), removing any prior cached RSC promise.
Issues a fresh HTTP request directly to the server, re-executing server components and page functions with latest data.
Usage Examples:
// Ideal for live feeds, stock tickers, or carts
<Link href="/live-feed" fresh>
Live Feed
</Link>// Navigate after a mutation to force fresh server data
const handleUpdate = async () => {
await updateProfile(data);
router.push("/profile", { fresh: true });
};4.3. Comparison: When to use which mechanism
| Mechanism / Scope | Cache Behavior | Network Request | Typical Use Case |
|---|---|---|---|
| Standard <Link> | Uses pageCache if prefetched or visited | 0ms if in cache; single request on first visit | General navigation, blogs, docs, marketing pages |
| <Link fresh> / { fresh: true } | Purges pageCache.delete(url) | Always fetches live RSC payload from server | Post-mutation redirects, active carts, volatile feeds |
| router.refresh() | Purges pageCache.delete(currentPath) | Re-fetches current route in-place | Polling, server function response handling, reload button |
| Hard Reload (F5) | Destroys all memory (all caches lost) | Full HTML document download + re-hydration | Emergency fallback; loses all client state |
6. Relative Routing Rules
Dinou resolves relative navigation paths (such as about, ./about, or ../dashboard) using a Directory-First Convention. This provides intuitive navigation that accurately mirrors your file system.
In a standard web browser, a URL like /blog/hello (without a trailing slash) treats hello as a file name, so a link to about resolves to /blog/about.
In Dinou, every route is treated as a directory because physically, each page is a folder containing a page.tsx component (e.g. src/blog/hello/page.tsx). Resolving relatively from a page navigates relative to that route's folder:
| Current Path | Relative Target | Resolved Path |
|---|---|---|
| /blog/hello | about | /blog/hello/about |
| /blog/hello | ./about | /blog/hello/about |
| /blog/hello | ../about | /blog/about |
This rule applies uniformly across all navigation methods, including the <Link> component and programmatic calls like router.push().