Primate LogoPrimate

Client navigation

Use primate/client to navigate from browser code without a full page reload. It is useful for interactions that happen outside a link, such as a desktop notification click.

import client from "primate/client";

await client.navigate("/projects/42");

client.navigate() is available only in browser code. Call it from a hydrated frontend component, event handler, or other client-side module; do not call it while rendering on the server.

Destinations

Pass either a relative or absolute URL:

await client.navigate("projects/42");
await client.navigate("/projects/42?tab=activity");
await client.navigate("https://app.example.com/projects/42");

Relative URLs resolve from the current page. Same-origin destinations use Primate's SPA navigation: Primate fetches and updates the current frontend without a full document reload, updates browser history, and scrolls to a hash target when one is present.

External destinations use normal browser navigation instead:

await client.navigate("https://example.com");

This lets application code use the same method without checking whether a URL belongs to the current app.

Notification example

For example, navigate when a notification is clicked:

import client from "primate/client";

notification.addEventListener("click", () => {
  void client.navigate(`/projects/${project.id}`);
});

Use void when the caller does not need to wait for the navigation to finish.

Refreshing the current route

Use refresh to fetch fresh server data for the current pathname and query without changing the URL, adding a history entry, scrolling, or reloading the document:

import client from "primate/client";

const refreshed = await client.refresh();

The promise resolves to true after Primate applies the fresh route data and dispatches the updated event. It resolves to false if the request fails, is superseded by another navigation, redirects to another URL, or does not return a successful Primate route response. In those cases, the current view, URL, and history remain unchanged.

refresh is distinct from navigating to the current URL. A same-URL client.navigate() call keeps its existing no-op behavior.

Primate automatically intercepts ordinary same-origin links after hydration, so you usually do not need to call client.navigate() from an anchor's click handler. For custom link handling, pass the click event as the optional second argument:

link.addEventListener("click", event => {
  void client.navigate(link.href, event);
});

For same-origin URLs, the event is prevented and the SPA navigation runs. For external URLs, the event is left untouched so the browser performs its normal link navigation.

Query strings and hashes

Query strings and hashes are preserved:

await client.navigate("/search?q=primate#results");

Navigating to a different query string on the current route performs an SPA navigation. Navigating to a hash on the current route updates history and scrolls to the matching element.

Shallow navigation

Use shallow to update the query string without fetching, remounting the current view, or scrolling:

await client.navigate("/search?q=primate", { shallow: true });

The public request store updates immediately. Back and Forward update it again without fetching while traversal remains within the same rendered view. Use replace when the new URL should replace the current history entry:

await client.navigate("/search?q=framework", {
  replace: true,
  shallow: true,
});

Shallow navigation applies only when the destination has the current pathname. A different pathname performs normal SPA navigation.

See Frontends for frontend setup and the available hydrated frontend integrations.

Previous
Intro
Next
Angular