v146 · Web APIs · Precommit vs Post-Commit

Precommit vs Post-Commit

The navigate event fires before the URL commits. The handler function inside event.intercept() runs after it commits. These two phases have different URL state and different APIs available — choose the right phase for each task.

Checking Navigation API support…

phase comparison

precommit — navigate event body

  • location.href still shows the old URL
  • navigation.currentEntry still the old entry
  • event.destination.url is the new URL
  • can call event.preventDefault() to cancel
  • can call event.intercept() to register handler
  • synchronous — runs before any commit

Good for:

  • start loading spinner
  • abort in-flight fetch requests
  • cancel the navigation (preventDefault)
  • set up context shared with handler

post-commit — handler() body

  • location.href shows the new URL
  • navigation.currentEntry is the new entry
  • event.destination.url still available (closure)
  • cannot cancel — URL already committed
  • can be async — awaited before navigatesuccess
  • address bar already shows new URL to user

Good for:

  • fetch new page content
  • update app state / store
  • render new route component
  • stop loading spinner

live comparison

Navigate and watch URL state in each phase

Precommit snapshot
location.href: —
navigation.currentEntry: —
event.destination: —
Post-commit snapshot
location.href: —
navigation.currentEntry: —
event.destination (closure): —
Navigate between pages to see the comparison…

code

navigation.addEventListener('navigate', event => {
  if (!event.canIntercept) return;

  // === PRECOMMIT (navigate event body) ===
  // URL has NOT changed yet
  console.log('pre:  location.href         =', location.href);        // old URL
  console.log('pre:  currentEntry.url      =', navigation.currentEntry.url); // old URL
  console.log('pre:  event.destination.url =', event.destination.url); // new URL ✓

  // Capture what we need for the handler (closure)
  const destUrl = event.destination.url;

  event.intercept({
    async handler() {
      // === POST-COMMIT (handler body) ===
      // URL HAS changed
      console.log('post: location.href         =', location.href);        // new URL ✓
      console.log('post: currentEntry.url      =', navigation.currentEntry.url); // new URL ✓
      console.log('post: destUrl (closure)     =', destUrl);              // still works

      // Now safe to load content for the new URL
      const html = await fetch(destUrl).then(r => r.text());
      renderContent(html);
    }
  });

  // After event.intercept(), you're still precommit here
  showLoadingSpinner(); // correct phase for this task
});

see also