Navigation
useNavigate, useParams, useStep, and how each one moves you around
flemo gives you three navigation hooks. They cover different shapes of movement.
useNavigate
Move between routes: push / replace / pop
useParams
Read the current route's params
useStep
Change params inside the current route
useNavigate
const navigate = useNavigate();
navigate.push("/posts/:slug", { slug: "hello" });
navigate.replace("/login");
navigate.pop(); // back one screen
navigate.pop({ skip: 2 }); // back two screens, in one transition
navigate.pop({ until: "/posts/:slug" }); // back to the nearest matching screen
navigate.pop({ until: "/", transitionName: "cupertino" }); // override the back transitionpush, replace, and pop are async. They wait for the transition to start and the route to
take effect. However far one reaches, it drives a single transition.
Reaching past the top
pop, replace, and push all take an optional distance ({ skip }, a number of screens,
or { until }, a route pattern) to reach a screen below the top in one transition. The screens
skipped over are removed without ever painting, so you never see them flash by.
{ skip: n } is the screen n below the top; { until } is the nearest screen below the top
whose route matches the pattern (searched top → root, current top excluded). They reach the same
target and differ only in what they do there:
| Method | At the reached target… |
|---|---|
pop | lands on it. The target stays |
replace | replaces it. The target and everything above become the new screen |
push | keeps it. The new screen stacks on top |
// stack: / → /library → /posts/a → /posts/b (top = /posts/b)
navigate.pop({ skip: 2 }); // → /, /library (lands on /library)
navigate.replace("/x", {}, { skip: 2 }); // → /, /x (/library replaced)
navigate.push("/x", {}, { skip: 2 }); // → /, /library, /x (/library kept)
navigate.pop({ until: "/library" }); // → /, /library
navigate.replace("/x", {}, { until: "/library" }); // → /, /x
navigate.push("/x", {}, { until: "/library" }); // → /, /library, /x{ skip } and { until } are mutually exclusive (until wins if both are passed). An unmatched
until is a no-op for pop / replace, and a plain push for push. { skip } clamps to the
stack depth.
Options
navigate.push(
"/posts/:slug",
{ slug: "hello" },
{
transitionName: "material",
layoutId: "photo-hello"
}
);| Option | What it does |
|---|---|
transitionName | Override the transition for this navigation. On pop it sets the back animation. Useful when collapsing several screens whose own transition isn't the one you want |
layoutId | Pair with transitionName: "layout" on push or replace to enable shared-element morphs |
skip | Reach n screens past the top in one transition. See Reaching past the top |
until | Reach back to the nearest screen matching a route pattern. See Reaching past the top |
skip, until, and transitionName are accepted by push, replace, and pop; layoutId
applies to push and replace.
useParams
useParams<T>() returns the params for the current route, typed against your RegisterRoute
augmentation.
function Post() {
const { slug } = useParams<"/posts/:slug">();
return <h1>{slug}</h1>;
}flemo merges path params and query params into one object.
URL: /posts/hello?ref=home
useParams<"/posts/:slug">() → { slug: "hello", ref: "home" }useStep
useStep() is for sub-states within the current screen. It pushes a new history entry (so the
browser back button still goes back) but doesn't change the route. The same <Screen> stays
mounted; only its params update.
A common case: a multi-step form on one screen.
function Onboarding() {
const { step = "name" } = useParams<"/onboarding">();
const stepper = useStep<"/onboarding">();
if (step === "name") {
return <button onClick={() => stepper.pushStep({ step: "email" })}>Next</button>;
}
if (step === "email") {
return <button onClick={() => stepper.popStep()}>Back</button>;
}
}| Method | What it does |
|---|---|
pushStep(params) | Push a new history entry with these params, same route |
replaceStep(params) | Replace current history entry with these params |
popStep() | Go back one step (same as window.history.back()) |
Choosing between push and step
| You want to… | Use |
|---|---|
| Show a different screen | navigate.push |
| Replace the current screen (e.g., login → home) | navigate.replace |
| Close the current screen | navigate.pop |
| Reach several screens back at once | navigate.pop({ skip }) |
| Reach back to a specific earlier screen | navigate.pop({ until }) |
| Push/replace while clearing screens in between | …({ skip }) / …({ until }) |
| Stay on the same screen but change UI | useStep().pushStep |
useStep keeps everything in the screen tree. Same Screen instance, same Provider, same
scroll position. useNavigate mounts a new Screen with a real transition.