smolter
API

Link

Navigate within the app using a normal anchor.

Compatibility

ReactAvailable in this renderer's browser integration.
React SSRAvailable to this renderer's SSR integration; browser event handlers run after hydration.
PreactAvailable in this renderer's browser integration.
Preact SSRAvailable to this renderer's SSR integration; browser event handlers run after hydration.

Import from @kasperrt/smolter/react @kasperrt/smolter/preact

Signature

function Link(options: LinkProps): ReactNode;

Parameters

Use to or href for the destination. replace chooses history replacement. Standard anchor attributes and onClick are forwarded.

Returns

An anchor with internal-navigation handling.

Usage

Import this API from one of the entry points listed above. Keep server and build helpers in their dedicated subpaths.

<Link href="/tasks/1">Open task</Link>
<Link href="/tasks?sort=title" replace>Sort by title</Link>

Behavior and edge cases

Modified clicks, downloads, external URLs, and non-_self targets retain native browser behavior. onClick runs first and can preventDefault. URL resolution follows normal URL rules relative to the current URL; this is not route-hierarchy-relative resolution.

Prefetching

prefetch accepts intent (the default), viewport, or none. Intent loads code on hover, focus or touch. Viewport uses IntersectionObserver once per mounted link; unsupported browsers skip it. Add prefetchData for safe speculative loader reads through createDataRouter. Actions and root loaders are never prefetched. No capability function imports or router configuration are needed.

<Link to="/tasks" prefetch="viewport" prefetchData>Tasks</Link>
<Link to="/settings" prefetch="intent">Settings</Link>
<Link to="/private" prefetch="none">Private</Link>

Data reuse and invalidation

Prefetch reads use reason: "prefetch" and the normal clientLoader/server loader transport. The cache is private to one data router, holds at most 16 URLs, and expires after 30 seconds from request start. Path and query identify an entry; hashes share data. A navigation consumes the pending or completed result once. Mutations, explicit revalidation and router disposal invalidate and abort all cached reads. Reads are not cached during tracked mutations. Errors and Response values (including redirects) are retried on navigation; speculative failures never navigate, reload or consume recovery markers. Leaving the viewport does not cancel a shared read; unmounting disconnects observation, and router disposal cancels pending reads.

await router.prefetch("/tasks?sort=title");
// Later navigation reuses fresh data without a duplicate loader.
await router.navigate("/tasks?sort=title#results");

Vite selection and typed spreads

Smolter’s Vite plugin uses TypeScript types to select intent handlers, viewport observation and data caching independently. A plain Link counts as intent usage. A union or typed spread that permits intent and viewport includes both. Later attributes override earlier spreads. Unknown types, broad strings and component-value escapes retain capabilities conservatively. Source is analyzed before JSX compilation, using the project tsconfig. Smolter owns a pinned stable TypeScript 6 compiler-API dependency (typescript-compiler) for build analysis, independent of the application’s TypeScript version (including TypeScript 7); it does not enter browser output. Without Smolter’s Vite plugin, all capabilities remain available. Changing the selected set in development reloads the page to update existing router instances.

const props = { prefetch: "viewport" } as const;
<Link {...props} /> // viewport only

declare const dynamic: { prefetch: "intent" | "viewport" };
<Link {...dynamic} /> // both
<Link {...dynamic} prefetch="none" /> // neither for this link

Deferred prefetched data

A clientLoader may return nested promises. Prefetch reuses that same payload and its promise identities; navigation does not wait for optional fields. The consuming navigation takes ownership of cancellation. A nested rejection remains deferred field error data for Await/use; unlike a top-level prefetch failure, it does not discard otherwise usable payload data. Speculative reads do not execute actions or trigger deployment recovery.

View implementation ↗