demo · v141

Route Interceptor

A four-route single-page app wired to the Navigation API's preCommitHandler. Toggle the interceptor on, pick a precommit action (auth check, data prefetch, or analytics), then navigate between routes. The timeline shows exactly when the navigate event fires, when precommit work runs, and when the URL finally commits — or aborts.

checking window.navigation…
0 ms
URL:

Navigation event details

No navigation yet. Click a route above.

Precommit timeline

the preCommitHandler pattern

navigation.addEventListener("navigate", (event) => {
  if (!event.canIntercept) return;
  if (new URL(event.destination.url).origin !== location.origin) return;

  event.intercept({
    // NEW in Chrome 141: preCommitHandler runs BEFORE the URL flips
    async preCommitHandler({ commit }) {
      log("precommit start");

      // Auth check, data prefetch, analytics — all before commit:
      await authCheck();                // throws to abort the navigation
      const data = await prefetchData(event.destination.url);

      commit();   // ← URL flips NOW; old page still visible until handler() runs
    },

    // handler() runs AFTER commit — swap the DOM here
    async handler() {
      renderNewRoute(location.search);
    },
  });
});

why this matters

Before preCommitHandler, intercept() committed immediately when the navigate event finished dispatching — the URL would flip before your async handler had fetched any data. This forced frameworks to show a loading skeleton on the new page while data loaded. With preCommitHandler, the URL stays on the old page (with the old scroll position and focus) until you call commit(), matching how browsers handle cross-document navigations internally. React Router's loader pattern and Remix's data-loading model now map naturally onto the Navigation API.

see also