Link
Import Link from goribu. It renders an anchor that works before JavaScript loads:
import { Link } from "goribu";
// Keep normal anchor behavior such as opening a link in a new tab.
export default function Menu() {
return (
<nav>
<Link href="/posts">Posts</Link>
<Link href="/login">Sign in</Link>
<Link href="/reports" prefetch={false}>Reports</Link>
</nav>
);
}
import { Link } from "goribu";
// Keep normal anchor behavior such as opening a link in a new tab.
export default function Menu() {
return (
<nav>
<Link href="/posts">Posts</Link>
<Link href="/login">Sign in</Link>
<Link href="/reports" prefetch={false}>Reports</Link>
</nav>
);
}
- History replacement:
replaceupdates the current history entry instead of adding one. - Prefetch default:
prefetchdefaults totrue. Hovering briefly or pressing an eligible link starts loading its destination. - Disable prefetch: Use
prefetch={false}when prefetching is not useful or is costly. - Anchor props:
Linkaccepts normal anchor props, includingtarget,download, and event handlers. - Click cancellation: Your
onClickruns first and can cancel navigation withpreventDefault().
NavLink
The NavLink is useful for a menus that need active url styling:
import { NavLink } from "goribu";
// Highlight the current section using pathname-based matching.
export default function SettingsMenu() {
return (
<nav>
<NavLink
href="/settings"
className={({ isActive }) => isActive ? "active" : undefined}
>
Settings
</NavLink>
<NavLink href="/settings/profile" end>Profile</NavLink>
</nav>
);
}
import { NavLink } from "goribu";
// Highlight the current section using pathname-based matching.
export default function SettingsMenu() {
return (
<nav>
<NavLink
href="/settings"
className={({ isActive }) => isActive ? "active" : undefined}
>
Settings
</NavLink>
<NavLink href="/settings/profile" end>Profile</NavLink>
</nav>
);
}
- Active state: An active
NavLinkgetsaria-current="page". LetNavLinkown that attribute. - Path matching: Matching ignores query strings and hashes, respects segment boundaries and normally includes child paths.
- Exact matching:
endrequires the exact pathname. The/pathname is always matched exactly.
Navigate from an event
Use navigate() when navigation follows an action with no meaningful anchor:
import { navigate } from "goribu";
// Continue after an application-controlled event.
export async function continueToDashboard() {
await navigate("/dashboard", { replace: true });
}
import { navigate } from "goribu";
// Continue after an application-controlled event.
export async function continueToDashboard() {
await navigate("/dashboard", { replace: true });
}
- Return value:
navigate()returns a Promise. - Link semantics: Prefer
Linkwhen users should be able to copy the URL, open another tab or navigate without JavaScript.
After changing data with your own fetch, navigate to the current URL to refresh a Worker-rendered page:
await fetch(`/posts/${id}/like`, { method: "POST" });
await navigate(location.href);
await fetch(`/posts/${id}/like`, { method: "POST" });
await navigate(location.href);
- Worker-rendered refresh: Navigating to the current URL runs the page's GET handler and
metaagain, fetches fresh route data and updates the page in place. - State and history: A refresh with it of the current URL keeps component state and adds no history entry.
replaceis not needed. - Pre-rendered refresh: A prerendered page has fixed build-time data. Navigating to its current URL only scrolls to the top.
- Form results: A Form result already updates the page itself.
Where navigation gets its data
When you follow a Link, call navigate or press Back/Forward in the browser, Goribu fetches the destination page's data and swaps it in without a full document load. Where that data comes from depends on how the destination route renders:
- Prerendered pages are served from Static Assets in production. The Worker does not run. The page receives the same props and metadata that were saved at build time and changing the query string does not recompute them. This also applies to prefetch and to redirects that land on a static page.
- Dynamic pages are requested from the Worker on every navigation, just like a document load. Middleware, GET handlers, cookies and request-scoped resources such as
req.d1all run as usual.
If a page must reflect the current request, such as the signed-in user or the query string, make it dynamic with a handler, middleware or prerender = false. See Rendering for the rules.
In development every navigation goes through the Worker, because build-time snapshots do not exist yet. Test static behavior with a production build.
In both cases, Goribu loads the destination's JavaScript and styles before it updates the screen and URL. The data request uses an internal endpoint under /_goribu/. Always navigate with Link, NavLink, or navigate instead of calling it directly. There is no router store or navigation hook to keep in sync.
Page state across navigation
Changing a query parameter keeps the pathname, so navigation updates the page in place and preserves its component state:
import { navigate } from "goribu";
export async function showNotesTab() {
const url = new URL(location.href);
url.searchParams.set("tab", "notes");
await navigate(url.href);
}
import { navigate } from "goribu";
export async function showNotesTab() {
const url = new URL(location.href);
url.searchParams.set("tab", "notes");
await navigate(url.href);
}
- Same-path navigation: Query changes such as
?tab=notes, revisiting the current URL and in-place Form renders update the page in place, preserving component state and DOM. - State across paths: To keep state across different paths of the same route, put it in the URL or browser storage.
History and scrolling
- History entries: Each navigation adds one history entry or replaces the current one with
replace. A redirect chain records only its final destination. - Scrolling: A new page scrolls to its hash target or to the top. Back and Forward restore the saved scroll position.
- Current URL: Navigating to the URL already on screen adds no history entry and scrolls to the top. A static page keeps its state without refetching while a dynamic page fetches fresh data.
Native behavior and fallbacks
- Browser-handled links: Goribu handles only same-origin links. External links, downloads,
target="_blank", modifier clicks and same-page#hashlinks behave as usual. - Full-page fallback: If a client-side transition can't complete, for example because the response is an API result or the app was redeployed, Goribu loads the page normally instead.
- Overlapping navigations: The latest navigation wins. A slower earlier request never replaces it.