Skip to content

Navigation

λ Router provides hooks for programmatic navigation, history traversal, and URL search parameter management. These hooks wrap the browser’s native Navigation API methods.

The useNavigate hook returns a function for navigating to a URL programmatically:

import { useNavigate } from "@studiolambda/router/react";
function LoginButton() {
const navigate = useNavigate();
async function handleLogin() {
await performLogin();
navigate("/dashboard");
}
return <button onClick={handleLogin}>Log in</button>;
}

The navigate function accepts an optional second argument with Navigation API options:

// Replace the current history entry instead of pushing.
navigate("/dashboard", { history: "replace" });
// Pass state to the destination.
navigate("/checkout", { state: { cartId: "abc123" } });

The return value is a NavigationResult from the Navigation API, which contains committed and finished promises you can await:

const result = navigate("/next-page");
await result.committed; // URL bar has updated.
await result.finished; // Navigation fully complete (transitions done).

The useNavigation hook returns the raw Navigation object from the Navigation API. Use this for advanced scenarios not covered by higher-level hooks:

import { useNavigation } from "@studiolambda/router/react";
function DebugInfo() {
const navigation = useNavigation();
const entries = navigation.entries();
return <pre>{JSON.stringify(entries.map((e) => e.url))}</pre>;
}

Navigate backward in the history stack:

import { useBack } from "@studiolambda/router/react";
function BackButton() {
const { back, canGoBack } = useBack();
return (
<button onClick={() => back()} disabled={!canGoBack}>
Back
</button>
);
}

The canGoBack boolean reflects whether there is a previous history entry to navigate to. The back() function accepts optional Navigation API options.

Navigate forward in the history stack:

import { useForward } from "@studiolambda/router/react";
function ForwardButton() {
const { forward, canGoForward } = useForward();
return (
<button onClick={() => forward()} disabled={!canGoForward}>
Forward
</button>
);
}

Returns the current URL pathname:

import { usePathname } from "@studiolambda/router/react";
function Breadcrumbs() {
const pathname = usePathname();
const segments = pathname.split("/").filter(Boolean);
return (
<nav>
{segments.map((segment, i) => (
<span key={i}>
{i > 0 && " / "}
{segment}
</span>
))}
</nav>
);
}

Returns the dynamic route parameters extracted from the matched URL pattern:

import { useParams } from "@studiolambda/router/react";
function UserProfile() {
const { id } = useParams();
return <h1>User #{id}</h1>;
}

The returned object maps parameter names (from :name or *name segments) to their captured string values.

Returns the current search parameters and a setter function for updating them:

import { useSearchParams } from "@studiolambda/router/react";
function FilteredList() {
const [searchParams, setSearchParams] = useSearchParams();
const sort = searchParams.get("sort") ?? "newest";
function changeSort(newSort: string) {
setSearchParams({ sort: newSort });
}
return (
<div>
<select value={sort} onChange={(e) => changeSort(e.target.value)}>
<option value="newest">Newest</option>
<option value="oldest">Oldest</option>
<option value="popular">Popular</option>
</select>
</div>
);
}

The setter accepts multiple formats:

// Object form — replaces all params.
setSearchParams({ sort: "newest", page: "2" });
// URLSearchParams instance.
setSearchParams(new URLSearchParams("sort=newest&page=2"));
// Updater function — receives current params.
setSearchParams((current) => {
current.set("page", String(Number(current.get("page") ?? "1") + 1));
return current;
});

By default, setSearchParams uses history: "replace" to avoid polluting the history stack with every parameter change. You can override this:

setSearchParams({ page: "2" }, { history: "push" });

Returns true while a navigation transition is in progress. Use this for loading indicators:

import { useIsPending } from "@studiolambda/router/react";
function NavBar() {
const isPending = useIsPending();
return (
<nav className={isPending ? "nav loading" : "nav"}>
{isPending && <ProgressBar />}
{/* links */}
</nav>
);
}

Returns the type of the most recent navigation:

import { useNavigationType } from "@studiolambda/router/react";
function PageTransition({ children }) {
const type = useNavigationType();
// type: "push" | "replace" | "reload" | "traverse" | null
const animation =
type === "traverse" ? "slide" : type === "push" ? "fade" : "none";
return <div className={`transition-${animation}`}>{children}</div>;
}

Returns the AbortSignal from the current navigation event. The signal aborts when the navigation is cancelled (e.g., by a new navigation starting). Pass this to fetch() calls to automatically cancel stale requests:

import { useNavigationSignal } from "@studiolambda/router/react";
function DataPage() {
const signal = useNavigationSignal();
useEffect(() => {
fetch("/api/data", { signal })
.then((res) => res.json())
.then(setData)
.catch((err) => {
if (err.name !== "AbortError") throw err;
});
}, [signal]);
return <div>{/* ... */}</div>;
}

Returns null before any navigation has occurred.