demo · v141
Lazy Route Redirect
Auth-gated route. The user navigates to /secret; before the URL commits, a precommit handler checks auth, decides "nope", and redirects the still-pending navigation to /login?next=/secret. The URL bar never blinks past the protected URL — the entire flow is one navigation entry.
history.replaceState — leaves a brief "you tried to see /secret" leak in the address bar and double-counts the page view. Precommit fixes both.
the call
navigation.addEventListener("navigate", (e) => {
if (!e.canIntercept || e.hashChange) return;
e.intercept({
// NEW in 141: precommit phase — URL still hasn't flipped
async precommitHandler(controller) {
const url = new URL(e.destination.url);
if (url.pathname.startsWith("/secret") && !isAuthed()) {
// Redirect the pending nav. URL bar will commit to the new URL,
// not the original one.
await controller.redirect("/login?next=" + encodeURIComponent(url.pathname));
}
},
async handler() {
await renderRoute(location.pathname);
},
});
});
why this angle
The WICG navigation-api issue #66 — the one the chromestatus entry directly cites as motivating this feature — was filed by routing-library authors. React Router, TanStack Router, Vue Router all need to gate navigation on async checks (auth, data prefetch, route guard) before the URL commits. Without precommit, the URL flipped first and routers had to choose between a flash of the wrong content or shimming with replaceState afterward. Precommit lets the router intercept, redirect or cancel, all without the URL ever showing the user something they're not allowed to see.