<Link> renders a plain <a> server-side, so it works with JavaScript off, on slow links, and during the brief moment before hydration completes. Once Pylon’s runtime is live, it intercepts clicks and does a client-side navigation instead of a full page reload.
What it does
The two stages differ because of cost. Chunks are content-hashed and served as
immutable, so warming one costs its bytes once per browser and makes every later visit instant. It is cheap enough to do as soon as a link appears. The page payload costs a server render every time, so it waits for a real signal of intent. Warming payloads on sight would spend one render per visible link on every page load and throw away nearly all of them.
The payload is held in memory only, never in a disk cache, so a reload always re-renders. It is single-use and short-lived, and it is dropped when a navigation commits.
The React root persists across navigations, so a layout shared between the old and new route keeps its instance. State, scroll position, and video playback all survive the navigation.
A page that takes longer than ~100ms to arrive shows the nearest
loading.tsx over the page area while it loads. Faster navigations swap straight to content, so the skeleton never flashes.API
className, target, rel, or onClick, passes through.
Examples
Basic
Disable prefetch
prefetch={false} turns off both stages. Use it when a link is unlikely to be followed and you do not want to warm anything for it:
dynamic(), which keeps the heavy component out of the route’s chunk entirely. prefetch={false} only delays the download until the click, where the user is waiting for it.
Open in a new tab
Programmatic navigation
<Link> is the prop-based API. For imperative navigation, for example after a form submit or a post-login redirect, call the global runtime:
window.__pylon is defined as soon as hydration completes. Use the ?. form to stay safe on the first render. Pylon has no RSC boundary, so there is no "use client" directive. Every page component already hydrates on the client.
How navigation works
- The user clicks a
<Link>. The runtime’s delegated handler listens fora[data-pylon-link]clicks. - The runtime uses the payload a hover already fetched, or it fetches the target URL now. Either way, the request carries
X-Pylon-Nav: 1, which tells the server this is a navigation, not a document load. - The server still runs the render. This is what resolves the page’s
serverDatareads. But the server replies with only the head metadata and the__PYLON_DATA__JSON (component, layouts, props, data), not the rendered markup, since the client re-renders from that data anyway. This is roughly half the bytes of a full page on a typical route. - The runtime looks up the new component in the bundle manifest and dynamically imports its entry chunk.
- The entry chunk’s
hydrate(component, Page, Layouts)populates the route cache without re-rendering (the cache is keyed on component path). - The runtime calls
root.render(buildTree(Page, Layouts, newProps)). React’s reconciler diffs against the existing tree. It reuses shared layouts and swaps only the page subtree. - The runtime calls
history.pushStateand scrolls to top. Navigation is done.
window.location.href = href. The browser then handles the navigation as a normal document load.
Differences from Next.js’s <Link>
The hash-link and
scroll props are on the roadmap.